0

I've recently worked on a reusable network service class for a service-aggregator iOS app. This class should retry a failed request if it was caused by expired user token. Plus, this class will be used between multiple contractors, who will create the aggregated services.

Since the contractors might use different authentication methods, I create a interface / protocol for the class that will manage user's authentication. Here's a sample of my work (in Swift):

BaseNetworkService.swift

class BaseNetworkService {

/**
Request headers that will be used by this instance.
*/
internal var requestHeaders = ["Content-Type": "application/json"]

/**
Request GET method to `URL` with passed `parameters`. Will send success response
in `next` and failure in `error` event of returned `RACSignal` instance.
*/
internal func GET(URL: String, parameters: [String: AnyObject]) -> RACSignal {
    // method implementation here
}

// Rest of HTTP methods here

}

AuthenticatedNetworkService.swift

class AuthenticatedNetworkService: BaseNetworkService {

/**
Used to retrieve authentication-related request headers
and refresh expired user token.
*/
private var authService: AuthenticationProtocol

init(authService: AuthenticationProtocol) {
    self.authService = authService
}

/**
Request GET method to `URL` with passed `parameters`. Will send success response
in `next` and failure in `error` event of returned `RACSignal` instance.

- note: If the request fails because of expired user token, this instance will
refresh current user token, and retry it once again.
*/
override func GET(URL: String, parameters: [String: AnyObject]) -> RACSignal {
    // method implementation here
}

// Rest of HTTP methods here    
}

AuthenticationProtocol.swift

protocol AuthenticationProtocol {

/**
Stores authentication header name in the `key`, and its value in its
corresponding `value`.
*/
internal var authenticationHeaders: [String: String] { get }

/**
Checks whether passed `error` caused by expired user token or not.
*/
func isErrorCausedByExpiredToken(error: NSError) -> Bool

/**
Refresh this instance's `authenticationHeaders`. It send `next:` event from 
returned `RACSignal` if the process succeeds, and `error:` otherwise.
*/
func refreshAuthenticatioHeaders() -> RACSignal

}

I was reading about Design Patterns while working on this, and created a Factory-like class for creating the AuthenticatedNetworkService for the aggregated services. Something around this:

class AuthenticatedNetworkServiceFactory {

/**
Returns network service for Pizza Delivery service.
*/
class func PizzaDeliveryNetworkService() -> AuthenticatedNetworkService {
    let authService = PizzaDeliveryAuthenticationService()
    return AuthenticatedNetworkService(authService: authService)
}

/**
Returns network service for Quick Laundry service.
*/
class func QuickLaundryNetworkService() -> AuthenticatedNetworkService {
    let authService = QuickLaundryAuthenticationService()
    return AuthenticatedNetworkService(authService: authService)
}

/**
Returns network service for Cab Finder service.
*/
class func CabFinderNetworkService() -> AuthenticatedNetworkService {
    let authService = CabFinderAuthenticationService()
    return AuthenticatedNetworkService(authService: authService)
}

}

Yet, after revisiting the GoF book, I found that Factory pattern was meant to return subclasses instead of a the main class. Since I was returning the main class (AuthenticatedNetworkService), is the AuthenticatedNetworkServiceFactory could be considered as a Factory? Or was it just a Helper class?

3
  • A factory class is supposed to take the decision which class to use off your hands. That doesn't mean it could not occasionally choose the base class, if that's all that is required. But if there is only one class in the picture ever, then there is no question of class-choosing to begin with, so the factory concept doesn't really apply. Commented Dec 7, 2015 at 7:50
  • @KilianFoth Thank you for the comment! Then - my class was just another Helper class, no? That being said, I made the so-called Factory class to hide the creation of corresponding AuthenticationServices from the client. Is that a valid point to create a class like this?
    – edopelawi
    Commented Dec 7, 2015 at 8:08
  • It's certainly not an abstract factory, but depending on who you ask it might be a simple factory. And it's always helpful to create abstractions to reduce the overall effort in expressing something - not every creational pattern is a factory (chooses types), there are also useful patterns that just help you collect arguments (facade, builder). Commented Dec 7, 2015 at 8:14

1 Answer 1

1

There is no clear answer to your question, because the definition of a factory is not cast in stone.

Many people consider a factory to be anything that creates something for you so that you don't have to new it yourself. This is dumb in my opinion, but who am I to say that your factories are not true factories ?

The real benefits of using a factory come when:

  • The factory decides which particular subclass to instantiate (as Kilian Foth mentioned) so that you don't have to know the actual class of the object created.

  • The factory is an abstract class itself, so not only you don't have to know the actual class of the object created, but you don't even know the actual class of the factory that you are invoking.

So, according to these real benefits™ your factory is in fact a helper, but if you still go ahead and call it a factory I do not think anybody can blame you. By calling it a factory you are simply advertising the fact that "this thang instantiates stuff".

2
  • I suppose whoever downvoted this will be leaving a comment any moment now to explain why, right? (chirping grasshoppers...)
    – Mike Nakis
    Commented Dec 7, 2015 at 16:09
  • 1
    Hey, thanks for the answer! I agree that GoF explains about abstract factory and factory method in their book, but not about the general factory term. I just trying to not look too dumb while applying the patterns :|
    – edopelawi
    Commented Dec 8, 2015 at 6:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.