0

I am new to programming in Swift and I have reached a blocker in constructing a Factory design pattern. Below is my code:

protocol IInterface {
   func InterfaceMethod() -> Void
}

public class SubClass: IInterface {
   init() {}

   func InterfaceMethod() { }

   public func SubClassMethod() { }
}

public class Factory() {
    class func createFactory() -> IInterface {
         return SubClass()
    }
}

Finally, i am trying to access it like below

let mgr = Factory.createFactory()

//calling Interface Method
mgr.InterfaceMethod() -- This works, when i keep a dot (mgr.) it shows the method name
//calling subclass method
mgr.SubClassMethod() -- This doesn't work, when i keep a dot (mgr.) it doesnt even show the subclass method name

Even if i use mgr.SubClassMethod, it throws error saying

Value of type IInterface has no member SubClassMethod

I believe i am getting exposed only with protocol methods though a SubClass object is returned via factory

All the examples I browsed and have seen shows only how to consume methods specified by the protocol, but i haven't seen an example where it shows how to consume a subclass's own methods apart from protocols methods

mixel
  • 25,177
  • 13
  • 126
  • 165
MaK
  • 81
  • 1
  • 7

3 Answers3

5

You miss the point of Factory pattern. The idea of Factory pattern is to provide methods that have specific return type and return instances that either has this type or inherits from this type (if it's class type) or conforms to this type (if it's protocol).

protocol Animal {
    func voice() -> String
}

class Dog: Animal {
    func voice() -> String {
        return "bark"
    }

    func sit() {
    }
}

class Cat: Animal {
    func voice() -> String {
        return "meow"
    }
}

class AnimalFactory {
    func getAnimal() -> Animal {
        return Dog()
    }
}

Client code calling Factory method should not speculate about its return value and try to cast it to concrete class. That's totally ruining the point of using Factory pattern.

As you can see in above example AnimalFactory.getAnimal() return instance of some type that conforms to Animal protocol. Code calling this method does not know and should not know about specific type of that instance.

If code calling Factory method expects that returning instance has type Dog or inherits from this type then you should create and use separate DogFactory:

class EvilDog: Dog {
    override func voice() -> String {
        return "bark-bark"
    }
}

class DogFactory {
    func getDog() -> Dog {
        return EvilDog()
    }
}

You can have situation when client code implement different behavior dependent on real type of instance that is returned by factory method. In that case AnimalFactory should implement methods for providing instances of all types that will be used in client code:

class AnimalFactory {
    func getDog() -> Dog {
        return EvilDog()
    }

    func getCat() -> Cat {
        return Cat()
    }
}

func trainAnimal(iLikeDogs: Bool, animalFactory: AnimalFactory) {
    if iLikeDogs {
        let dog = animalFactory.getDog()
        dog.voice()
        dog.sit() // only dog can sit
    } else {
        let cat = animalFactory.getCat()
        cat.voice()
    }
}

Actually there are three patterns - Factory, Abstract Factory and Factory method. You can read about differences here.

Community
  • 1
  • 1
mixel
  • 25,177
  • 13
  • 126
  • 165
  • 1
    The core problem in this answer is that also `EvilDog` dogs reply to `.sit()`, this is not a probable behaviour. Possibly add `var obedient : Bool = true` property to `Dog`, and flip to `false` for `EvilDog`, thereafter modifying the `.sit()` function to return a boolean that is this `obedient` property. Jokes aside, good post, +1! ;) – dfrib Jan 16 '16 at 14:03
1

A protocol can be conformed to by several entities (in your case, class SubClass), but the protocol itself doesn't know about which entities that are conforming to it. Since your createFactory() method returns a type (protocol) IInterface, not type SubClass, the return as is will not know about members specific to SubClass, even if a SubClass object is sent as return.

let mgr = Factory.createFactory() /* Type: let mgr: IInterface */

This is also apparent when attempting to call a member SubClassMethod (or any unknown member, say .foo, for that metter) on instance mgr

error: value of type 'IInterface' has no member 'SubClassMethod'

To wrap it up, a type conforming to a protocol will have access to all blueprinted methods and properties in that protocol, but an instance of a protocol used as a type (which is acceptable if you don't have Self or any associated types in your protocol) will know nothing about the methods and properties of other types that happens to conforms to that protocol.


As mentioned below, if you know the return of type IInterface is of a certain type, then you can attempt a type conversion (as?) to this type, e.g. ... as? SubClass. Note however again that the protocol IInterface doesn't know about which types that conforms to it, to this type conversion can't be asserted as successful in compile time; this is strictly something to be controlled by you as developer, possibly leading to runtime exceptions if you're not careful (e.g. using un-safe methods such as forced conversion as!). Now, if the function always returns a SubClass type, you might as well change its signature to

class func createFactory() -> SubClass {

As possible use case of keeping the IInterface return is if createFactory() actually do return different types that all conform to the IInterface protocol. In that case, you could safely use a switch block over the createFactory() return to perform type conversion to you different known types (known by developer) that conform to IInterface, as follows

protocol IInterface {
    func InterfaceMethod() -> Void
}

public class SubClass: IInterface {
    init() {}

    func InterfaceMethod() { }

    public func SubClassMethod() { }
}

public class AnotherClass: IInterface {
    init() {}

    func InterfaceMethod() { }

    public func AnotherClassMethod() { }
}

public class Factory {
    class func createFactory() -> IInterface {

        if arc4random_uniform(5) > 2 {
            return SubClass()
        }
        else {
            return AnotherClass()
        }
    }
}

switch block over return when calling you factory method:

switch(Factory.createFactory()) {
case let mgr as SubClass:
    print("Subclass")
    mgr.SubClassMethod()
case let mgr as AnotherClass:
    print("AnotherClass")
    mgr.AnotherClassMethod()
// default case
case let mgr as IInterface:
    print("Unknown specific regarding return type")
}

Finally, possibly the answer to the following SO question can be of value for you

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192
0

Your let mgr is of type IInterface that indeed does not have SubClassMethod.

If you know that Factory.createFactory() returns SubClass you can cast it to SubClass like that:

let mgr = Factory.createFactory() as! SubClass

Then let mgr will be of type SubClass and you should be able to call mgr.SubClassMethod.

Nevertheless, your example has nothing to do with factory design pattern even though you named class Factory. Take a look at @mixel's answer for details.

kostek
  • 801
  • 2
  • 15
  • 32
  • Your answer contradicts the concept of Factory pattern. – mixel Jan 16 '16 at 11:38
  • 1
    @mixel You're right, but OP didn't specify question explicitly and my answer at least helps to compile his example. Hope that added explanation makes it more clear. – kostek Jan 16 '16 at 16:33