0

I am trying to design and implement a framework to communicate with server (it's an iOS framework written in Swift). The challenge I am facing is the architecture - there are two ways of communicating with the server and I have to implement both (different versions). I really want to achieve having a stateless client, with methods such as: Client.authenticate() or Client.downloadFile(). The problem is when having two implementations I would end up with methods in my Client class like this one:

public class func authenticate(state: state) {
    if (state.type == 1) {
        Client1.authenticate(state)
    } else {
        Client2.authenticate(state)
    }
}

Repeated for every single method...

I wanted to initially keep the client like this - stateless and static and have only state objects that hold the actual state as there could be many connections to the server with various states. By that I wanted to avoid having the client as an object and both holding the state and performing the calls to the server. The problem is that this approach is just...dirty I guess. What would be a more DRY, readable and sustainable way of doing this?

Michal
  • 15,429
  • 10
  • 73
  • 104

1 Answers1

1

I don't fully understand your intention without more code samples, but the patterns I will present to you will surely clear things up for you.

If your Client class always uses either Client1 or Client2 (or more specifically, if your every client object state variable doesn't change through it's instances lifetime) you should use Dependency Injection.

You create a procol (Let's call it RemoteClient with authenticate method (and every other method that the server client should implement) and make Client1 and Client2 conform to that protocol.

Now you make your Client class to accept a RemoteClient in it's constructor.

Now whatever creates the Client object, it can decide what to inject into the constructor: the Client1 concrete class object, or Client2.

There's a lot of articles about Dependency Injection so I won't cover it in much detail.

Example article

You can also use the Strategy design pattern, which is very similiar but kind of different in intent:

Strategy design pattern

Difference between DI and Strategy

EDIT

After you've clarified what you want to do in comments below:

In that case, you can use reflection/metadata and use dictionary/map to invoke the client you want.

(pseudocode)

enum ServerType 
{ 
   client1,
   client2
}

 Dictionary* serversDictionary; // key = ServerType , value = object of protocol type RemoteLocation

static init
 {
    serversDictionary[client1] = Client1.self; // using swift class metadata
    serversDictionary[client2] = Client2.self; // using swift class metadata  
  }

static authenticate(ServerType type) {

 let locationToSendAuthTo = serversDictionary[type]; 
 locationToSendAuthTo.authenticate(type);
}

I'm not sure if Swift works that way because I've just started using it. I'm not sure if you can call a static method on a class type. The docs are pretty thin on that.

More here: Swift class introspection & generics

Community
  • 1
  • 1
michal.ciurus
  • 3,616
  • 17
  • 32
  • Thank you for your answer!I gave up on my first initial idea and actually did what you suggested.I can see I failed to clarify the core thing I wanted to achieve - Client being a static class(not an initialized object).Just like I would call POST in Alamofire(without initializing the client) I wanted to call methods on the static client(injecting the state everytime).The challenge was how to do this when I need two implementations of the client - I would have to have a 'router' in order to call `Client.performSth()` without the "user" of the SDK having to specify which one(Client1 or Client2). – Michal Feb 27 '15 at 09:49
  • Then the way how to determine which client to use would be through the state object. But that would generate the kind of method I posted in my question - for every method I would want to expose - same thing with switch, calling the correct implementation... – Michal Feb 27 '15 at 09:50