工厂方法模式与组合
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.
思路是让subclasses决定实例化哪个class,而GoF的实现思路,就是在superclass中提供一个抽象方法,即subclasses需要覆盖,从而提供自己的实例化逻辑。
我不明白的是,为什么要使用抽象方法来实现它,而不是使用 是 工厂的 class 成员。我来解释一下。
这是 GoF 的方式:
public abstract class Creator {
public void doStuff() {
Product product = createProduct();
/* etc. */
}
public abstract Product createProduct();
}
用法:
MyCreator myCreator = new Creator() {
@Override
public Product createProduct() {
return new MyProduct();
}
}
myCreator.doStuff();
但是为什么要为 subclassing 等烦恼呢?我建议这种方法:
public class Creator {
private Factory factory;
/* constructor */
public Creator(Factory factory) {
this.factory = factory;
}
public void doStuff() {
Product product = factory.createProduct();
/* etc. */
}
}
这就是您的使用方式:
MyFactory myFactory = new MyFactory();
Creator creator = new Creator(myFactory);
creator.doStuff();
那么 GoF 方法的好处是什么?为什么不把Factory组合成Creatorclass,而是用抽象方法表达呢?
Why not composing the Factory into the Creator class instead of expressing it using an abstract method?
因为在那种情况下,客户端 "knows" 实例化什么工厂 class 会破坏模式的全部目的。整个想法是客户不知道工厂是什么,它只调用抽象 class。认为您将某个第三方工厂提供商插入到您的框架中,并且您不知道他们实例化(也不关心)什么,只要能达到目的。
在这些类型的情况下,使用实际的英语类型进行说明通常很有用,而不是像 "Product"
这样更抽象的术语
所以,让我们以宠物为例,假设每种(子)类型的宠物都有一个家(例如,狗有一个狗窝)。
对于您的示例,我们将有:
Public void doStuff() {
Home home = factory.createHome();
}
但是在这种情况下,说狗得到狗舍而鸟得到笼子的逻辑在哪里?它必须进入工厂对象——这反过来意味着 Pets 的继承层次结构必须在工厂逻辑中明确重复。这反过来意味着每次添加新类型的宠物时,您都必须记住将该类型也添加到工厂中。
工厂方法模式意味着你会有类似的东西:
public class Pet {
private String name;
private Home home;
public Pet(String name) {
this.name = name;
this.home = this.createHome();
}
public abstract Home createHome();
这允许您每次创建新类型的宠物时,都可以指定该宠物类型的家而无需更改任何其他类型 class,并且在此过程中还知道每个宠物类型都有一个家。
例如。你会:
public class Dog extends Pet {
public Home createHome() {
return new Kennel();
}
}
编辑以添加狗示例
工厂方法模式用于让子classes 控制它们生成的对象类型。在您的情况下,控制对象类型的不是 subclass,而是 class.
的用户
以下示例是从the Wikipedia entry on the Factory Method Pattern 复制的。我们有一个迷宫游戏,它有两种变体:普通迷宫游戏和修改规则的魔法迷宫游戏。普通迷宫游戏由普通房间和魔法房间的魔法迷宫游戏组成
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 由 MagicRooms 组成,但我们仍然可以重复使用部分 superclass.
根据您的建议,MazeGame class 将更改为:
public class MazeGame {
public MazeGame(RoomFactory factory) {
Room room1 = factory.makeRoom();
Room room2 = factory.makeRoom();
room1.connect(room2);
this.addRoom(room1);
this.addRoom(room2);
}
}
现在可以做:
MazeGame game1 = new MazeGame(new MagicRoomFactory());
MazeGame game2 = new MagicMazeGame(new OrdinaryRoomFactory());
这是您希望避免的事情,因为这样可以创建包含错误房间类型的迷宫游戏。
我认为这个问题的答案并不完全令人满意,所以这是我的两分钱...
引用@Hoopje 的回答,我们可能会得到这样的结果:
public class MazeGame {
public MazeGame(RoomFactory factory) {
Room room1 = factory.makeRoom();
Room room2 = factory.makeRoom();
room1.connect(room2);
this.addRoom(room1);
this.addRoom(room2);
}
}
然后
MazeGame game1 = new MazeGame(new MagicRoomFactory());
MazeGame game2 = new MagicMazeGame(new OrdinaryRoomFactory());
如上所述,这是荒谬的。但是,我认为最后两行无论如何都没有意义,因为在 OP 看来,您根本不需要创建 MagicMazeGame。相反,您只需实例化一个传入 new MagicRoomFactory() 的 MazeGame。
MazeGame magicOne = new MazeGame(new MagicRoomFactory());
MazeGame normalOne = new MazeGame(new OrdinaryRoomFactory());
但是,假设我们的 MazeGame 有多个工厂。比方说,除了需要有一个房间工厂,它还需要有一个积分工厂,还有一个怪物工厂。在这种情况下,我们最终可能会将所有这些都强制放入 MazeGame 构造函数中,如下所示:
public MazeGame(RoomFactory rmFactory, PointsFactory pointsFactory, MonsterFactory monstersFactory)
这相当复杂,最好创建具体的 sub类 来控制自己的游戏类型。那个长的构造函数对我来说也像是一种代码味道,尽管更有知识的人可能会添加到这个。
更重要的是,这意味着无论哪个对象正在实例化游戏,都必须了解所有这些内部工作 类,这会破坏封装。
总而言之,通过这种方式我们给了客户太多的控制权,他们不想知道所有这些实现细节。
反正我是这么看的...
来自 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.
思路是让subclasses决定实例化哪个class,而GoF的实现思路,就是在superclass中提供一个抽象方法,即subclasses需要覆盖,从而提供自己的实例化逻辑。
我不明白的是,为什么要使用抽象方法来实现它,而不是使用 是 工厂的 class 成员。我来解释一下。
这是 GoF 的方式:
public abstract class Creator {
public void doStuff() {
Product product = createProduct();
/* etc. */
}
public abstract Product createProduct();
}
用法:
MyCreator myCreator = new Creator() {
@Override
public Product createProduct() {
return new MyProduct();
}
}
myCreator.doStuff();
但是为什么要为 subclassing 等烦恼呢?我建议这种方法:
public class Creator {
private Factory factory;
/* constructor */
public Creator(Factory factory) {
this.factory = factory;
}
public void doStuff() {
Product product = factory.createProduct();
/* etc. */
}
}
这就是您的使用方式:
MyFactory myFactory = new MyFactory();
Creator creator = new Creator(myFactory);
creator.doStuff();
那么 GoF 方法的好处是什么?为什么不把Factory组合成Creatorclass,而是用抽象方法表达呢?
Why not composing the Factory into the Creator class instead of expressing it using an abstract method?
因为在那种情况下,客户端 "knows" 实例化什么工厂 class 会破坏模式的全部目的。整个想法是客户不知道工厂是什么,它只调用抽象 class。认为您将某个第三方工厂提供商插入到您的框架中,并且您不知道他们实例化(也不关心)什么,只要能达到目的。
在这些类型的情况下,使用实际的英语类型进行说明通常很有用,而不是像 "Product"
这样更抽象的术语所以,让我们以宠物为例,假设每种(子)类型的宠物都有一个家(例如,狗有一个狗窝)。
对于您的示例,我们将有:
Public void doStuff() {
Home home = factory.createHome();
}
但是在这种情况下,说狗得到狗舍而鸟得到笼子的逻辑在哪里?它必须进入工厂对象——这反过来意味着 Pets 的继承层次结构必须在工厂逻辑中明确重复。这反过来意味着每次添加新类型的宠物时,您都必须记住将该类型也添加到工厂中。
工厂方法模式意味着你会有类似的东西:
public class Pet {
private String name;
private Home home;
public Pet(String name) {
this.name = name;
this.home = this.createHome();
}
public abstract Home createHome();
这允许您每次创建新类型的宠物时,都可以指定该宠物类型的家而无需更改任何其他类型 class,并且在此过程中还知道每个宠物类型都有一个家。
例如。你会:
public class Dog extends Pet {
public Home createHome() {
return new Kennel();
}
}
编辑以添加狗示例
工厂方法模式用于让子classes 控制它们生成的对象类型。在您的情况下,控制对象类型的不是 subclass,而是 class.
的用户以下示例是从the Wikipedia entry on the Factory Method Pattern 复制的。我们有一个迷宫游戏,它有两种变体:普通迷宫游戏和修改规则的魔法迷宫游戏。普通迷宫游戏由普通房间和魔法房间的魔法迷宫游戏组成
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 由 MagicRooms 组成,但我们仍然可以重复使用部分 superclass.
根据您的建议,MazeGame class 将更改为:
public class MazeGame {
public MazeGame(RoomFactory factory) {
Room room1 = factory.makeRoom();
Room room2 = factory.makeRoom();
room1.connect(room2);
this.addRoom(room1);
this.addRoom(room2);
}
}
现在可以做:
MazeGame game1 = new MazeGame(new MagicRoomFactory());
MazeGame game2 = new MagicMazeGame(new OrdinaryRoomFactory());
这是您希望避免的事情,因为这样可以创建包含错误房间类型的迷宫游戏。
我认为这个问题的答案并不完全令人满意,所以这是我的两分钱...
引用@Hoopje 的回答,我们可能会得到这样的结果:
public class MazeGame {
public MazeGame(RoomFactory factory) {
Room room1 = factory.makeRoom();
Room room2 = factory.makeRoom();
room1.connect(room2);
this.addRoom(room1);
this.addRoom(room2);
}
}
然后
MazeGame game1 = new MazeGame(new MagicRoomFactory());
MazeGame game2 = new MagicMazeGame(new OrdinaryRoomFactory());
如上所述,这是荒谬的。但是,我认为最后两行无论如何都没有意义,因为在 OP 看来,您根本不需要创建 MagicMazeGame。相反,您只需实例化一个传入 new MagicRoomFactory() 的 MazeGame。
MazeGame magicOne = new MazeGame(new MagicRoomFactory());
MazeGame normalOne = new MazeGame(new OrdinaryRoomFactory());
但是,假设我们的 MazeGame 有多个工厂。比方说,除了需要有一个房间工厂,它还需要有一个积分工厂,还有一个怪物工厂。在这种情况下,我们最终可能会将所有这些都强制放入 MazeGame 构造函数中,如下所示:
public MazeGame(RoomFactory rmFactory, PointsFactory pointsFactory, MonsterFactory monstersFactory)
这相当复杂,最好创建具体的 sub类 来控制自己的游戏类型。那个长的构造函数对我来说也像是一种代码味道,尽管更有知识的人可能会添加到这个。
更重要的是,这意味着无论哪个对象正在实例化游戏,都必须了解所有这些内部工作 类,这会破坏封装。 总而言之,通过这种方式我们给了客户太多的控制权,他们不想知道所有这些实现细节。
反正我是这么看的...