Factory Method pattern vs composition
从GOF关于工厂方法模式的章节:
Define an interface for creating an object, but let subclasses decide which class to
instantiate. Factory Method lets a class defer instantiation to subclasses.
其思想是让子类决定要实例化哪个类,而GOF的思想实现是在超类中提供一个抽象方法,子类需要重写,从而提供自己的实例化逻辑。
我不明白的是,为什么要使用抽象方法实现它,而不使用工厂类成员。我会解释的。
这是GOF方式:
1 2 3 4 5 6 7 8 | public abstract class Creator { public void doStuff() { Product product = createProduct(); /* etc. */ } public abstract Product createProduct(); } |
用途:
1 2 3 4 5 6 7 8 | MyCreator myCreator = new Creator() { @Override public Product createProduct() { return new MyProduct(); } } myCreator.doStuff(); |
但是为什么要费心子类化等呢?我建议这种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Creator { private Factory factory; /* constructor */ public Creator(Factory factory) { this.factory = factory; } public void doStuff() { Product product = factory.createProduct(); /* etc. */ } } |
这就是你如何使用它:
1 2 3 | MyFactory myFactory = new MyFactory(); Creator creator = new Creator(myFactory); creator.doStuff(); |
那么,GoF的方法有什么好处呢?为什么不将工厂组成Creator类而不是使用抽象方法来表示它呢?
工厂方法模式用于给子类控制它们生成的对象的类型。在您的例子中,控制对象类型的不是子类,而是类的用户。
下面的示例是工厂方法模式的维基百科条目的副本。我们有一个迷宫游戏,它有两个变体:普通迷宫游戏和一个有适应规则的魔法迷宫游戏。普通迷宫游戏包括普通房间和魔术房间的魔术迷宫游戏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class MazeGame { public MazeGame() { Room room1 = makeRoom(); Room room2 = makeRoom(); room1.connect(room2); this.addRoom(room1); this.addRoom(room2); } protected Room makeRoom() { return new OrdinaryRoom(); } } public class MagicMazeGame extends MazeGame { @Override protected Room makeRoom() { return new MagicRoom(); } } |
通过使用工厂方法模式,我们可以确保magicmazegame由magicRoom组成,但是我们仍然可以重用超类的部分。
在您的建议中,mazegame类将改为:
1 2 3 4 5 6 7 8 9 | public class MazeGame { public MazeGame(RoomFactory factory) { Room room1 = factory.makeRoom(); Room room2 = factory.makeRoom(); room1.connect(room2); this.addRoom(room1); this.addRoom(room2); } } |
现在有可能做到:
1 2 | MazeGame game1 = new MazeGame(new MagicRoomFactory()); MazeGame game2 = new MagicMazeGame(new OrdinaryRoomFactory()); |
这是你想要避免的事情,因为这样就可以创建一个有错误房间的迷宫游戏。
Why not composing the Factory into the Creator class instead of expressing it using an abstract method?
因为在这种情况下,客户机"知道"要实例化的工厂类,这会破坏模式的整个目的。整个想法是,客户机不知道什么工厂是建立的,它只调用一个抽象类。假设您将某个第三方工厂提供程序插入到您的框架中,并且您不知道它们实例化了什么(也不关心),只要满足了目的。
在这种情况下,使用实际的英语类型来做插图,而不是像"产品"这样更抽象的术语,通常是有用的。
所以,让我们拿宠物来说,每一种(亚)类型的宠物都有一个家(例如,狗有一个犬舍)。
对于您的示例,我们将:
1 2 3 | Public void doStuff() { Home home = factory.createHome(); } |
但在这种情况下,说狗得到狗窝,鸟得到笼子的逻辑去了哪里?它必须进入工厂对象——这意味着宠物的继承层次结构必须在工厂逻辑中明确地重复。这反过来意味着,每次你添加一种新类型的宠物,你将不得不记住添加到工厂的类型以及。
工厂方法模式意味着您将拥有如下内容:
1 2 3 4 5 6 7 8 |
这允许每次创建一个新的宠物类型时,您可以指定宠物类型的家,而不必更改任何其他类,并且在此过程中还知道每个宠物类型都有一个家。
你应该有:
1 2 3 4 5 | public class Dog extends Pet { public Home createHome() { return new Kennel(); } } |
编辑以添加狗示例