工厂方法模式与组合

时间:2015-02-27 14:42:58

标签: java design-patterns factory-pattern

关于工厂方法模式的GoF章节:

  

定义用于创建对象的接口,但让子类决定使用哪个类   实例即可。 Factory Method允许类将实例化延迟到子类。

这个想法是让子类决定实例化哪个类,而GoF实现的想法是在超类中提供一个抽象方法,子类需要覆盖,从而提供自己的实例化逻辑。

我不明白的是,为什么使用抽象方法实现它,而不使用 工厂的类成员。我会解释一下。

这是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();

但为什么要打扰子类等?我建议采用这种方法:

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 组合到 Creator 类而不是使用抽象方法表达呢?

4 个答案:

答案 0 :(得分:3)

  

为什么不将Factory组合成Creator类而不是使用抽象方法表达它?

因为在这种情况下,客户"知道"什么工厂类来实例化这个模式的整个目的。整个想法是客户端忘记了工厂的构建,它只调用Abstract类。认为你插入到你的框架中的某个第三方工厂提供商并且你不知道他们实例化了什么(并且也不在乎)只要服务于目的。

答案 1 :(得分:3)

工厂方法模式用于为子类控制它们生成的对象类型。在您的情况下,它不是控制对象类型的子类,而是类的用户。

以下示例是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组成,但我们仍然可以重复使用超类的部分。

在您的建议中,MazeGame类将更改为:

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());

这是您想要阻止的内容,因为可以创建包含错误类型房间的MazeGames。

答案 2 :(得分:1)

在这些类型的情况下,通常可以使用实际的英语类型进行插图,而不是像#34; Product"

等更抽象的术语。

所以,让我们来看看宠物,并说每个(子)类型的宠物都有一个家(例如,狗有一个狗窝)。

对于您的示例,我们将:

Public void doStuff() {
    Home home = factory.createHome();
}

但是在这种情况下,逻辑去哪里说Dogs得到狗窝,鸟得到笼子?它必须进入工厂对象 - 这反过来意味着必须在Factory逻辑中明确重复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();

这允许您每次创建新类型的宠物时,您可以指定宠物类型的家,而无需更改任何其他类,并且在此过程中也知道每个宠物类型都有一个家。

EG。你会:

public class Dog extends Pet {
    public Home createHome() {
        return new Kennel();
    }
}

编辑添加狗示例

答案 3 :(得分:0)

我认为这个问题的答案并不完全令人满意,所以这是我的两分钱......

引用@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) 

这是相当复杂的,相反,最好创建具体的子类,每个子类都控制自己的游戏类型。那个长的构造函数在我看来也像是代码的味道,尽管知识渊博的人可能会对此进行补充。

更重要的是,这意味着任何实例化游戏的对象都必须知道所有这些内部工作类,这破坏了封装。 总而言之,通过这种方式,我们给了客户太多的控制权,他们不想知道所有这些实施细节。

反正我是这么看的...