美文网首页
工厂模式

工厂模式

作者: Whyn | 来源:发表于2017-10-27 21:37 被阅读25次

    简介

    工厂模式 创建型 设计模式之一。简单来讲,对于代码中常用的创建对象操作,工厂模式使用工厂方法代替new操作。

    工厂模式包含 工厂产品工厂 生产 产品(需要生成的对象叫 产品,生成对象的地方叫 工厂)。

    工厂模式的核心就是解耦 需求产品

    使用场景

    任何需要生成 复杂对象 的地方,都可以使用工厂模式。

    在 Java 系统中,凡是创建对象new的地方,都可以尝试考虑下是否可以使用工厂模式代替。

    :上面说的 复杂对象 指的是类的构造函数参数过多等对于类的构造有影响这些情况,因为如果类的构造过于复杂,如果直接在其他业务类内使用,那么两者的耦合过重,后续业务更改,就需要在 任何 引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。而如果采用工厂方法,由于类的创建是在工厂类内的,后续如果要修改该类的创建或者直接替换掉该类,那么只需更改工厂类内产品创建即可,其他地方无需更改(当然这里的前提是要求产品类的对外接口保持一致,对于替换类,也要求对外接口跟之前的产品类保持一致 -- 这里可以通过接口/抽象类提供一致的行为作为产品类即可)。

    代码展示

    工厂模式,按实际业务场景进行划分,有3种不同的实现方式:

    简单工厂

    简单工厂 又叫做 静态工厂方法(Static Factory Method),简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品。

    简单工厂模式 作用:一个具体工厂生产许多产品;

    其 UML 类图如下所示:

    简单工厂

    简单工厂模式 通用代码如下所示(与上面 UML 实现有些许区别):

    //IProduct.java
    //抽象产品
    public interface IProduct {
        void doSomething();
    }
    //具体产品:ProductA
    class ProductA implements IProduct{
        @Override
        public void doSomething() {
            System.out.println("I am Product A");
        }
    }
    //具体产品:ProductB
    class ProductB implements IProduct{
        @Override
        public void doSomething() {
            System.out.println("I am Product B");
        }
    }
    //具体产品:ProductC
    class ProductC implements IProduct{
        @Override
        public void doSomething() {
            System.out.println("I am Product C");
        }
    }
    
    final class Const {
        static final int PRODUCT_A = 0;
        static final int PRODUCT_B = 1;
        static final int PRODUCT_C = 2;
    }
    
    @IntDef({Const.PRODUCT_A,
            Const.PRODUCT_B,
            Const.PRODUCT_C})
    @Retention(RetentionPolicy.SOURCE)
    @interface ProductKind {
    }
    
    //SimpleFactory.java
    public class SimpleFactory {
    
        public static IProduct makeProduct(@ProductKind int kind) {
            switch (kind) {
                case Const.PRODUCT_A:
                    return new ProductA();
                case Const.PRODUCT_B:
                    return new ProductB();
                case Const.PRODUCT_C:
                    return new ProductC();
            }
            return null;
        }
    } 
    

    所以通过SimpleFactory.makeProduct(kind)传入相应的标识就可以创建出对应的产品了。

    简单工厂优点/缺点

    • 优点:结构简单,调用方便。对于外界给定的信息,可以很方便的创建出相应的产品。工厂和产品的职责区分明确。
    • 缺点:工厂类单一,负责所有产品的创建,但产品基数增多时,工厂类代码会非常臃肿,违反高内聚原则。

    简单工厂适用场景
    对于 产品种类相对较少 的情况下,使用简单工厂可以很方便创建所需产品。

    工厂方法模式

    Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.
    定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类。

    工厂方法模式(Factory Method),又称为 多态性工厂模式

    工厂方法模式中,不再有单一的工厂类产生产品,而是有工厂类的子类实现具体产品的创建。因此,当增加多一个产品的时候,只需增加一个相应的工厂类子类实现生产这种产品即可,这便可以解决简单工厂生产太多产品导致其内部代码臃肿(switch case分支过多)的问题。

    工厂方法模式 作用:每个具体工厂生产一个具体产品;

    工厂方法模式适用场景

    • 对于 产品种类众多 的情形。

    其 UML 类图如下所示:

    工厂方法模式

    代码展示如下:

    //IFactory.java 
    //抽象工厂
    public interface IFactory {
        IProduct makeProduct();
    }
    //生产ProductA的具体工厂类
     class FactoryA implements IFactory {
    
        @Override
        public IProduct makeProduct() {
            return new ProductA();
        }
    }
    //生产ProductB的具体工厂类
    class FactoryB implements IFactory {
    
        @Override
        public IProduct makeProduct() {
            return new ProductB();
        }
    }
    
    //生产ProductC的具体工厂类
    class FactoryC implements IFactory {
    
        @Override
        public IProduct makeProduct() {
            return new ProductC();
        }
    }
    
    //IProduct.java 
    //抽象产品
    public interface IProduct {
        void doSomething();
    }
    
    //具体产品:ProductA
    class ProductA implements IProduct {
        @Override
        public void doSomething() {
            System.out.println("I am Product A");
        }
    }
    
    //具体产品:ProductB
    class ProductB implements IProduct {
        @Override
        public void doSomething() {
            System.out.println("I am Product B");
        }
    }
    
    //具体产品:ProductC
    class ProductC implements IProduct {
        @Override
        public void doSomething() {
            System.out.println("I am Product C");
        }
    }
    

    你需要哪个产品,就用相应的工厂类进行调用,比如我需要ProductA,那么调用:FactoryA.makeProduct()就可以得到一个ProductA

    工厂方法优点/缺点

    优点

    • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
    • 典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类无需关心,满足迪米特原则,依赖倒置原则,里氏替换原则。

    缺点

    • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
    抽象工厂模式

    Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
    为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。

    抽象工厂模式 是所有形态的工厂模式中最为抽象和最具一般性的描述的一种形态。

    适用场景
    抽象工厂模式适用于需要生成产品族的情景。抽象产品类内部提供了多个其他抽象产品,抽象工厂类定义了产品的创建接口,通过具体的工厂子类,就可以生产相应的产品族对象,供给用户端适用。

    其 UML 类图如下所示:


    抽象工厂模式

    Demo
    对于抽象工厂类,由于其是针对产品族创建产品的,因此我们这里举一个跟生活比较贴近的例子,让我们能更好地理解。

    由于抽象工厂创建产品族,因此,抽象工厂类提供的接口就是多个不同产品的输出,每个产品定义由于不同的工厂生产的产品也是不同的,因此产品的定义也是一个抽象的。比如现在我们想要一个冰箱和风扇,我们可以从生产这些家用设备的工厂(抽象工厂类)进行获取,但是每个工厂(具体的工厂类)生产的冰箱和风扇也是不同的,但是冰箱和风扇具备的功能描述是一样的,因此这里的冰箱和风扇应该也是用抽象进行表示,然后不同的具体工厂生产各自具体的冰箱和风扇。

    上面的说明纯粹从理论进行描述,下面给出了具体例子进行理解:

    假设我们想购买冰箱(抽象产品)和风扇(抽象产品),可以从美的公司(具体工厂)购买,也可以从格力公司(具体工厂)购买,甚至于从2个公司中各买一件产品也是可以的。

    结合上述例子中,我们就可以开始着手编写抽象工厂类实现:

    1. 由于我们需要冰箱和风扇,因此我们需要一个抽象工厂类提供冰箱和风扇:
    //抽象工厂类
    public interface IDeviceFactory {
        //生产冰箱
        IFridge makeFridge();
    
        //生产风扇
        IFan makeFan();
    }
    
    //IFridge.java
    //冰箱抽象
    public interface IFridge {
        //制冷性能
        int refrigerationPerformance();
    }
    //IFan.java 
    public interface IFan {
        //最大风力
        int max();
    } 
    
    1. 然后,我们可以去美的买或者去格力买:
    //具体工厂类:美的
    public class MideaFactory implements IDeviceFactory{
        @Override
        public IFridge makeFridge() {
            return new IFridge() {
                @Override
                public int refrigerationPerformance() {
                    System.out.println("美的冰箱");
                    return 0;
                }
            };
        }
    
        @Override
        public IFan makeFan() {
            return new IFan() {
                @Override
                public int max() {
                    System.out.println("美的风扇");
                    return 0;
                }
            };
        }
    }
    
    //具体工厂类:格力
    public class GreeFactory implements IDeviceFactory {
        @Override
        public IFridge makeFridge() {
            return new IFridge() {
                @Override
                public int refrigerationPerformance() {
                    System.out.println("格力冰箱");
                    return 1;
                }
            };
        }
    
        @Override
        public IFan makeFan() {
            return new IFan() {
                @Override
                public int max() {
                    System.out.println("格力风扇");
                    return 1;
                }
            };
        }
    } 
    

    然后,我们就可以按如下测试方法进行调用:

      @Test
        public void testAbstractFactory(){
            //从美的购买冰箱和风扇
            IDeviceFactory deviceFactory = new MideaFactory();
            IFridge mideaFridge = deviceFactory.makeFridge();
            IFan mideaFan = deviceFactory.makeFan();
            Truth.assertThat(mideaFridge.refrigerationPerformance()).isEqualTo(0);
            Truth.assertThat(mideaFan.max()).isEqualTo(0);
            //从格力购买冰箱和风扇 
            deviceFactory = new GreeFactory();
            IFridge greeFridge = deviceFactory.makeFridge();
            IFan greeFan = deviceFactory.makeFan();
            Truth.assertThat(greeFridge.refrigerationPerformance()).isEqualTo(1);
            Truth.assertThat(greeFan.max()).isEqualTo(1);
        }
    

    抽象工厂优点/缺点
    优点

    1. 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品族。
    2. 抽象工厂增强了程序的扩展性,对于新产品族的增加,只需实现一个新的具体工厂即可,无需更已有代码进行修改,符合开闭原则。

    缺点:抽象工厂结构相对复杂。

    总结

    对于少数产品的创建,使用简单工厂(一个具体工厂生产多个产品)。
    对于多数产品的创建,使用工厂方法(多个工厂生产对应一个产品)。
    对于产品族的创建,使用抽象工厂(多个工厂生产对应多个产品(族))。

    上一篇:单例设计模式

    相关文章

      网友评论

          本文标题:工厂模式

          本文链接:https://www.haomeiwen.com/subject/vhgdpxtx.html