工厂方法和抽象工厂模式.

一、概念

  • 工厂方法模式:用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。这样,关于超类的代码和子类创建对象的代码之间就解耦了。
  • 抽象工厂模式(Abstract Factory Pattern):提供一个接口,用来创建相关或依赖对象的家族,而不需要明确指定具体类。这样,产品创建的过程只会依赖于接口,而不关心具体的实现是什么,从而达到解耦的目的。
  • 角色 - 工厂方法模式:
     1、抽象工厂(Creator):定义了一个抽象的工厂方法,让子类实现此方法制造产品。
     2、具体工厂(ConcreteCreator):实现抽象工厂方法,包含具体生产产品的实现代码,返回一个产品的实例。
     3、抽象产品(Product):定义一个抽象的产品,为了工厂中创建产品类型的匹配。
     4、具体产品(ConcreteProduct):实现抽象产品,包含各自产品的特色代码。
  • 角色 - 抽象工厂模式:
     1、抽象工厂(Creator):一般是接口或抽象类,定义了一系列的产品家族。
     2、具体工厂(ConcreteCreator):实现抽象工厂方法,包含一系列产品家族的实现代码。
     3、抽象产品(Product):定义一个抽象的产品,为了工厂中创建产品类型的匹配。
     4、具体产品(ConcreteProduct):实现抽象产品,包含各自产品的特色代码。

二、Demo 实现 - 工厂方法模式

TOPIC:我们要定义一个披萨店,并允许个人或机构加盟,而且个人或机构可以根据当地不同的口味生产不同的披萨。

1、抽象工厂 - PizzaStore.java

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        System.out.println(pizza.name);
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    
    protected abstract Pizza createPizza(String type);
}

这里,我们定义了一个抽象工厂角色,并定义了一个抽象工厂方法。这样产品(Pizza)的实例化过程会交由抽象工厂的子类 —— 具体工厂去创建。

2、抽象产品 - Pizza.java

public abstract class Pizza {
    /**
     * 披萨的名字
     */
    protected String name;

    protected void bake() {
        System.out.println("烘烤25分钟...");
    }

    protected void cut() {
        System.out.println("把匹萨沿着对角线切开...");
    }

    protected void box() {
        System.out.println("包装用官方标准的盒子...");
    }
}

这里,我们定义了一个抽象产品角色,之所以把产品定义成抽象的,有两个原因:一是为了在具体工厂中生产的产品可以用统一的抽象父类来接收;二是为了让加盟商可以任意的扩展自己的产品。这种设计符合我们的设计原则 —— 面向接口(或抽象)编程,不面向实现编程。

3、具体产品

现在有加盟商要加盟我们的商店,他希望能够按照他们的地方特色,自定义他们要出售的披萨...那么,只要扩展 Pizza 类就可以了!

public class CheesePizza extends Pizza {
    public CheesePizza() {
        name = "这是一个奶酪披萨";
    }
    @Override
    protected void bake() {
        System.out.println("烘烤30分钟...");
    }
    @Override
    protected void cut() {
        System.out.println("把匹萨按四等分切开...");
    }
    @Override
    protected void box() {
        System.out.println("包装用奶酪披萨特制的盒子...");
    }
}
public class DurianPizza extends Pizza {
    public DurianPizza() {
        name = "这是一个榴莲披萨";
    }
    @Override
    protected void bake() {
        System.out.println("烘烤45分钟...");
    }
    @Override
    protected void cut() {
        System.out.println("把匹萨按三等分切开...");
    }
    @Override
    protected void box() {
        System.out.println("包装用榴莲披萨特制的盒子...");
    }
}

4、具体工厂 - PizzaFactory.java

public class PizzaFactory extends PizzaStore {
    
    @Override
    protected Pizza createPizza(String type) {
        switch (type) {
            case "cheese":
                return new CheesePizza();
            case "durian":
                return new DurianPizza();
            default:
                break;
        }
        return null;
    }
}

这里,我们定义了一个具体工厂角色,继承自抽象工厂,产品(Pizza)的具体实例化代码在这里实现。

5、测试

public class Test {

    public static void main(String[] args) {
        PizzaStore store = new PizzaFactory();
        store.orderPizza("cheese");
        store.orderPizza("durian");
    }
}

avatar

三、使用抽象工厂模式重构代码

1、抽象工厂 - AbstractFactory.java

public interface AbstractFactory {
    /**
     * @Description 奶酪披萨制造接口
     */
    Pizza createCheese();

    /**
     * @Description 榴莲披萨制造接口
     */
    Pizza createDurian();
}

这里,我们定义了一个抽象工厂角色,我们用接口定义了一系列的产品家族。

2、具体工厂

public class AFactory implements AbstractFactory {

    @Override
    public Pizza createCheese() {
        System.out.println("A工厂制造的奶酪披萨");
        return new CheesePizza();
    }

    @Override
    public Pizza createDurian() {
        System.out.println("A工厂制造的榴莲披萨");
        return new DurianPizza();
    }
}
public class BFactory implements AbstractFactory {

    @Override
    public Pizza createCheese() {
        System.out.println("B工厂制造的奶酪披萨");
        return new CheesePizza();
    }

    @Override
    public Pizza createDurian() {
        System.out.println("B工厂制造的榴莲披萨");
        return new DurianPizza();
    }
}

这是两个具体的工厂类,实现了抽象工厂接口,包含了一系列产品家族的实现代码。

3、抽象产品和具体产品

抽象产品和具体产品的代码和工厂方法模式一致,这里就不加赘述了,来看看抽象工厂的使用吧!

4、测试

public class PizzaStore {

    private AbstractFactory abstractFactory;

    public PizzaStore(AbstractFactory abstractFactory) {
        this.abstractFactory = abstractFactory;
    }

    public void prepare() {
        this.abstractFactory.createCheese();
        this.abstractFactory.createDurian();
    }
}
public class Test {

    public static void main(String[] args) {
        AbstractFactory abstractFactory = new AFactory();
        PizzaStore pizzaStore = new PizzaStore(abstractFactory);
        pizzaStore.prepare();
        AbstractFactory abstractFactory2 = new BFactory();
        PizzaStore pizzaStore2 = new PizzaStore(abstractFactory2);
        pizzaStore2.prepare();
    }
}

抽象工厂模式在使用时,利用对象组合和多态将不同的工厂实现类注入到代码中。这样,代码只会依赖接口,根本不关心具体实现是什么,以此达到解耦的目的。

avatar

演示源代码:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/factory

四、总结

  • 依赖倒置原则:要依赖抽象,不要依赖具体类 —— 不能让高层组件依赖底层组件,而且,不管是高层还是底层组件,都应该依赖于抽象。想要遵循依赖倒置原则,工厂模式并非是唯一的技巧,但却是最有威力的技巧之一。
  • 编写模块的实现依赖于抽象,在运行时传入具体的实现细节, 这就是依赖倒置的工作原理。依赖倒置原则教我们尽量避免使用具体类,而多使用抽象。
  • 依赖倒置原则的目的是让程序员脱离底层粘合代码,编写上层业务逻辑代码。这就让上层代码依赖于底层细节的抽象,从而可以重用上层代码。这种模块化和重用方式是双向的:既可以替换不同的细节重用上层代码,也可以替换不同的业务逻辑重用细节的实现。
  • 优点:
     1、封装变化。将创建对象的代码集中在一个对象或方法中,可以避免代码的重复。
     2、面向接口编程。在实例化对象时,向客户隐藏了实例化的细节,用户只需要关心所需产品对应的工厂,无需关心创建细节,甚至无须知道具体产品类的类名。
  • 缺点:
     1、系统中类的个数会增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销。
     2、由于考虑到系统的可扩展性,需要引入抽象层,增加了系统的抽象性和理解难度。
  • 工厂方法模式和抽象工厂模式的区别?
     1、工厂方法和抽象工厂的任务都是负责实例化对象,但是工厂方法用的方式是继承,而抽象工厂方式用的方法是对象组合。
     2、工厂方法注重于:把对象实例化的代码从具体类中解耦出来 —— 通过子类继承实现,或者目前还不知道将来需要实例化哪些具体类时;抽象工厂注重于:当你需要创建产品家族和想让制造的相关产品集中起来时。
posted @ 2018-11-20 15:20  JMCui  阅读(3480)  评论(1编辑  收藏  举报