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、工厂方法注重于:把对象实例化的代码从具体类中解耦出来 —— 通过子类继承实现,或者目前还不知道将来需要实例化哪些具体类时;抽象工厂注重于:当你需要创建产品家族和想让制造的相关产品集中起来时。
  • 相关阅读:
    ExtJS小技巧
    Oracle 表的行数、表占用空间大小,列的非空行数、列占用空间大小 查询
    NPM 私服
    IDEA 不编译java以外的文件
    SQL 引号中的问号在PrepareStatement 中不被看作是占位符
    Chrome 浏览器自动填表呈现淡黄色解决
    批量删除Maven 仓库未下载成功.lastupdate 的文件
    Oracle 11g 监听很慢,由于监听日志文件太大引起的问题(Windows 下)
    Hibernate 自动更新表出错 建表或添加列,提示标识符无效
    Hibernate 自动更新表出错 More than one table found in namespace
  • 原文地址:https://www.cnblogs.com/jmcui/p/9988320.html
Copyright © 2011-2022 走看看