【问题标题】:Factory Method pattern - what for this pattern is necessary?工厂方法模式 - 这种模式需要什么?
【发布时间】:2017-08-25 06:28:08
【问题描述】:

我有抽象类工厂 Factory 和工厂方法 getProduct() 和他的子类。

我有抽象类产品Product 和他的子类。

Classes-factories 创建了 classes-products 的对象。

abstract class Factory {
    abstract function getProduct();
}
class FirstFactory extends Factory {
    public function getProduct() {
        return new FirstProduct();
    }
}

abstract class Product {
};
class FirstProduct extends Product {
}

结果我可以使用这个客户端代码:

$factory = new FirstFactory();
$firstProduct = $factory->getProduct();
$factory = new SecondFactory();
$secondProduct = $factory->getProduct();

问题:这种模式需要什么?因为在客户端代码中我只能使用直接类:

$firstProduct = new FirstProduct();
$secondProduct = new SecondProduct();

【问题讨论】:

    标签: design-patterns factory-pattern


    【解决方案1】:

    重要的是要注意,工厂模式并不总是最好的解决方案,对于简单的情况,使用简单的new 会更好。例如,如果没有 Factory,您的示例可能会更好。 当您不需要处理类的确切实现但停留在“抽象层”(例如接口和抽象类)时,工厂模式会发挥其全部力量。您可以查看“依赖倒置原则 DIP”(依赖于抽象。不依赖于具体类)。

    工厂用例:一行切换数据库

    例如,假设您有一个使用数据库系统的软件。无论出于何种原因,您都知道使用的具体数据库(MongoDB、SQL...)可能会在以后更改。 (或者在开发过程中甚至只需要文件中的假硬编码数据库)。工厂模式允许您通过调用正确的工厂在一行中从一个切换到另一个,因为所有实现都依赖于抽象。 (这其实是DAO模式很好的利用了Factory Pattern,更多信息请看oracle文档:http://www.oracle.com/technetwork/java/dataaccessobject-138824.html

    具体示例:有 2 个派系的游戏

    这里是一个具体而简单的实现示例。

    你有 2 个单位

    • 苦工
    • 战士

    你有 2 个派系

    • 兽人
      • 兽人苦工
      • 兽人战士
    • 人类
      • 人类苦工
      • 人类战士

    2 名玩家

    • 兽人玩家(仅使用兽人单位)
    • 人类玩家(仅使用人类单位)

    您希望实例化两个播放器,但具体的播放器类应该以更通用的方式实现,以便以后可以重用。这在添加几个新派系时也非常重要,你不想花时间回到你的玩家类。

    代码示例

    要构建并运行它,复制 Main.java,然后复制 javac Main.javajava Main

    结果应该是

    // Factories ----------------------------------------
    
    abstract class AbsUnitFactory {
        public abstract Warrior creaWarrior();
        public abstract Peon creaPeon();
    }
    
    class OrcFactory extends AbsUnitFactory {
        public Warrior creaWarrior() {
            return new OrcWarrior();
        }
        public Peon creaPeon() {
            return new OrcPeon();
        }
    }
    
    class HumanFactory extends AbsUnitFactory {
        public Warrior creaWarrior() {
            return new HumanWarrior();
        }
        public Peon creaPeon() {
            return new HumanPeon();
        }
    }
    
    abstract class Unit {
        public abstract String getRole();
        public abstract String getFaction();
    
        @Override
        public String toString() {
            String str = new String();
            str += "[UNIT]\n";
            str += "    Role: " + this.getRole() + "\n";
            str += "    Faction: " + this.getFaction() + "\n";
            return str;
        }
    }
    
    
    // Warrior Units ----------------------------------------
    
    abstract class Warrior extends Unit {
        @Override
        public String getRole() {
            return "I'm a badass Warrior with the biggest sword!";
        }
    }
    
    class OrcWarrior extends Warrior {
        @Override
        public String getFaction() {
            return "Orc";
        }
    }
    
    class HumanWarrior extends Warrior {
        @Override
        public String getFaction() {
            return "Human";
        }
    }
    
    
    // Peon Units ----------------------------------------
    
    abstract class Peon extends Unit {
        @Override
        public String getRole() {
            return "I'm a little simple peon... Ready to work.";
        }
    }
    
    class HumanPeon extends Peon {
        @Override
        public String getFaction() {
            return "Human";
        }
    }
    
    class OrcPeon extends Peon {
        @Override
        public String getFaction() {
            return "Orc";
        }
    }
    
    
    // Main components ----------------------------------------
    
    class Player {
        private AbsUnitFactory  factory;
        private Peon            myPeon;
        private Warrior         myWarrior;
    
        public Player(AbsUnitFactory pFactory) {
            this.factory    = pFactory;
            this.myPeon     = this.factory.creaPeon();
            this.myWarrior  = this.factory.creaWarrior();
        }
    
        @Override
        public String toString() {
            return this.myPeon.toString() + this.myWarrior.toString();
        }
    }
    
    class Main {
        public static void main(String[] args) {
            AbsUnitFactory  humanFactory    = new HumanFactory();
            AbsUnitFactory  orcFactory      = new OrcFactory();
            Player          humanPlayer     = new Player(humanFactory);
            Player          orcPlayer       = new Player(orcFactory);
    
            System.out.println("***** Human player *****");
            System.out.println(humanPlayer.toString());
    
            System.out.println("***** Orce player *****");
            System.out.println(orcPlayer.toString());
        }
    }
    

    看看玩家类如何被任何派系重用,唯一定义女巫派系使用的行是工厂。 (你甚至可以添加一个单例)。

    更多资源

    这些是我非常欣赏的书(关于设计模式)

    【讨论】:

    • 这是抽象工厂模式的示例。
    • 如果我们没有苦工,只有勇士。然后类 Player 将创建 Warrior 的对象:OrcWarrior 或 HumanWarrio,取决于收到的参数。而且,如果我需要在 clent 代码中创建 OrcFactory 或 HumanFactory,那么这种模式有什么好处?因为我可以叫:新兽人战士;或新的人类战士;
    • 我可以使用:humanPlaywer = new Player(); Player.myWarrior = new HumanWarrior();我不明白模式工厂方法有什么好处
    • 有趣的类是player。不要将工厂模式视为一种替代“新”的方式,而更像是一种自动切换到正确的具体实现的方式。对于简单的情况,没有工厂可能会更好(所以是的Player.myWarrior = new HumanWarrior(); 可能更容易)。但现在想象一下,玩家有一个单位列表,他可以创建苦工、战士和 50 多种类型。假设还有精灵、矮人……使用工厂模式,你只有一个永远不会改变的 Player 类。只给出正确的工厂,所有单元的创建都是自动完成的。
    【解决方案2】:

    如果您在编译时知道 firstProduct 始终是 FirstProduct 类型,而 secondProduct 始终是 SecondProduct 类型,则不需要工厂方法。

    工厂方法仅在您想要创建可能是 FirstProduct 或可能是 SecondProduct 的产品时才有用,具体取决于工厂的运行时类型。例如,工厂的类型可能由用户输入决定。

    【讨论】:

      【解决方案3】:

      可以注入工厂而不是实际的类。假设您有一个只能在运行时根据某些特定条件实例化的类,在这种情况下您不能执行 new Foo(...args) 。 一种选择是注入 FooFactory 并让它为您创建 Foo 实例。

      【讨论】:

        猜你喜欢
        • 2023-04-06
        • 1970-01-01
        • 2010-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多