zoukankan      html  css  js  c++  java
  • 工厂方法和抽象工厂模式.

    一、概念

    • 工厂方法模式:用来封装对象的创建。工厂方法模式(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、工厂方法注重于:把对象实例化的代码从具体类中解耦出来 —— 通过子类继承实现,或者目前还不知道将来需要实例化哪些具体类时;抽象工厂注重于:当你需要创建产品家族和想让制造的相关产品集中起来时。
  • 相关阅读:
    骑士飞行棋 C#代码详解
    C#中的static静态变量的用法
    Break和Continue的一些注意事项
    枚举类型
    html 01-认识Web和Web标准
    css 17-CSS3的常见边框汇总
    css 16-浏览器的兼容性问题
    css 15-Sass入门
    css 14-CSS3属性详解:Web字体
    css 13-CSS3属性:Flex布局图文详解
  • 原文地址:https://www.cnblogs.com/jmcui/p/9988320.html
Copyright © 2011-2022 走看看