资讯专栏INFORMATION COLUMN

Java设计模式-工厂模式

Songlcy / 2935人阅读

摘要:一简单工厂定义简单工厂其实并不属于种设计模式之一,该模式是工厂方法模式的弱化或者说是工厂方法模式的一种特例,因为简单,所以称为简单工厂模式,也叫做静态工厂模式。

一、简单工厂 定义

简单工厂其实并不属于23种GOF设计模式之一,该模式是工厂方法模式的弱化(或者说是工厂方法模式的一种特例),因为简单,所以称为简单工厂模式(Simple Factory Pattern),也叫做静态工厂模式。虽然不是"标准"的设计模式(更像是一种编程习惯),但在实际项目中,采用该方法的案例还是比较多的。

简单工厂模式没有严格的定义,我们姑且使用以下描述:

提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类
关键点

具体的工厂类

创建产品的工厂方法

静态方法(静态工厂名字的由来)

通常会有一个"类型"参数(还有一种是可以提供多个静态工厂方法并通过不同方法名区别要创建的产品)

根据参数,利用if或者switch创建产品并返回

实现 抽象产品类(接口或抽象类)
public interface Product {
    void doSomething();
    void doAnything();
}
具体产品类
public class ConcreteProductA implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA doAnything");
    }
}
public class ConcreteProductB implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB doAnything");
    }
}
工厂类
public class Creator {

    public static Product createProduct(String type) {
        Product product = null;
        switch (type) {
            case "A":
                product = new ConcreteProductA();
                break;
            case "B":
                product = new ConcreteProductB();
                break;
        }
        return product;
    }
}
客户端代码
public class Client {

    public static void main(String[] args) {
        Product productA = Creator.createProduct("A");
        productA.doSomething();
        productA.doAnything();
      
        Product productB = Creator.createProduct("B");
        productB.doSomething();
        productB.doAnything();
    }

}
优点

简单

解耦

客户端不需要依赖具体的产品类,只依赖工厂类,传入不同的参数就可以得到不同的对象;

工厂方法可以根据用户条件返回同一类型的不同子类;

工厂方法中可以增加额外创建对象的细节,并且对客户端屏蔽这些细节。

缺点

工厂类的扩展比较困难,每增加一个产品,就要在工厂中添加相应的分支,对扩展开放的同时对修改也开放了,不符合开闭原则。如果有很多产品,那么工厂方法会显得特别"臃肿",降低可读性且不易维护。

使用场景

new的替代品,需要用new创建对象的地方都可以考虑简单工厂;

产品种类较少,并且基本可以预见不需要太多扩展的情况(产品需求基本已经确定,例如计算器,已知的运算类型大概就那么多种)

二、工厂方法 定义
Define an interface for creating an object,but let subclasses decide which class toinstantiate.Factory Method lets a class defer instantiation to subclasses.

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

实现 抽象产品类
public interface Product {
    void doSomething();
    void doAnything();
}
具体产品类
public class ConcreteProductA implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA doAnything");
    }
}
public class ConcreteProductB implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB doAnything");
    }
}
抽象工厂类
public interface Creator {
    Product createProduct();
}
具体工厂类
public class ConcreteCreatorA implements Creator {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}
public class ConcreteCreatorB implements Creator {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}
客户端代码
public static void main(String[] args) {
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.createProduct();
        productA.doSomething();
        productA.doAnything();

        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.createProduct();
        productB.doSomething();
        productB.doAnything();
    }
优点

良好的封装性,代码结构清晰

一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。

工厂方法模式的扩展性非常优秀

在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。

工厂方法模式是典型的解耦框架

高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类。

缺点

每增加一个产品类,就需要增加一个对应的工厂类,增加了额外的开发量。

扩展

利用反射机制来解决"每增加一个产品类,就需要增加一个对应的工厂类"的问题

抽象工厂类
public interface Creator {
     T createProduct(Class clazz);
}
具体工厂类
public class ConcreteCreator implements Creator {
    @Override
    public  T createProduct(Class clazz) {
        Product product= null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}
客户端代码
public class Client {
    public static void main(String[] args) {
        Creator creator = new ConcreteCreator();

        Product productA = creator.createProduct(ConcreteProductA.class);
        productA.doSomething();
        productA.doAnything();

        Product productB = creator.createProduct(ConcreteProductB.class);
        productB.doSomething();
        productB.doAnything();
    }
}
工厂方法 VS 简单工厂

工厂方法模式是对简单工厂的进一步抽象和解耦。和简单工厂比:

工厂方法模式多了一个抽象工厂类

每个产品都对应一个具体的工厂类

工厂方法模式把简单工厂的内部逻辑判断转移到了客户端代码来进行。各个实例对象的创建代码,没有耦合在同一个工厂类里。

工厂方法模式更易于扩展,克服了简单工厂会违背开闭原则的缺点,又保持了封装对象创建过程的优点。

三、抽象工厂 定义
Provide an interface for creating families of related or dependent objects without specifyingtheir concrete classes.

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。

实现 抽象产品类

产品A家族

public interface ProductA {
    void doSomething();
    void doAnything();
}

产品B家族

public interface ProductB {
    void doSomething();
    void doAnything();
}
具体产品类

产品A家族,产品等级1

public class ConcreteProductA1 implements ProductA {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA1 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA1 doAnything");
    }
}

产品A家族,产品等级2

public class ConcreteProductA2 implements ProductA {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA2 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA2 doAnything");
    }
}

产品B家族,产品等级2

public class ConcreteProductB1 implements ProductB {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB1 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB1 doAnything");
    }
}

产品B家族,产品等级2

public class ConcreteProductB2 implements ProductB {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB2 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB2 doAnything");
    }
}
抽象工厂类
public interface Creator {
    /**
     * 创建A产品家族
     * @return
     */
    ProductA createProductA();

    /**
     * 创建B产品家族
     * @return
     */
    ProductB createProductB();

    // ...
    // 有N个产品族,在抽象工厂类中就应该有N个创建方法
}

有N个产品族,在抽象工厂类中就应该有N个创建方法

具体工厂类
public class ConcreteCreator1 implements Creator {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}
public class ConcreteCreator2 implements Creator {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}

有M个产品等级,就应该有M个具体工厂实现

客户端代码
public class Client {
    public static void main(String[] args) {
        Creator creator1 = new ConcreteCreator1();

        ProductA productA1 = creator1.createProductA();
        productA1.doSomething();
        productA1.doAnything();

        ProductB productB1 = creator1.createProductB();
        productB1.doSomething();
        productB1.doAnything();

        Creator creator2 = new ConcreteCreator2();

        ProductA productA2 = creator2.createProductA();
        productA2.doSomething();
        productA2.doAnything();

        ProductB productB2 = creator2.createProductB();
        productB2.doSomething();
        productB2.doAnything();
    }
}
优点

封装性,高层模块不需要关心每个产品的实现类,不关心对象是如何创建出来的,只要知道工厂类是谁,就能创建出一个需要的对象

一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象,并且很容易交换产品系列

可以定义产品族内的约束,并且这样的约束对高层模块来说是透明的

缺点

抽象工厂模式的最大缺点就是产品族扩展非常困难,以上面的通用代码为例,如果要增加一个产品C,也就是说产品家族由原来的2个增加到3个,抽象工厂类Creator要增加一个方法createProductC(),然后每个实现类都要修改,违反了开闭原则。

注意是产品族扩展困难,而不是产品等级,如果新增一个产品等级,只需增加一个具体工厂类的实现即可完成扩展

抽象工厂 VS 工厂方法

抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

工厂方法模式生产一个产品,抽象工厂模式生产多个产品(一系列产品);在编程中,通常表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。

四、总结

简单工厂、工厂方法、抽象工厂这三种模式是逐步抽象的,后者适用于更为一般的场景,而前者是后者的特例。但它们的目标是殊途同归的,目的都是灵活地创建所需的对象并且对客户端隐藏对象创建细节,三者的扩展性和开发量有所不同,可以根据实际情况选择合适的模式来代替以new的方式创建对象的过程:

简单工厂适用于产品种类较少,且不需要太多扩展的场景

工厂方法模式作为简单工厂的进一步抽象和补充,更加适用于有很多扩展需求的场景

如果一个产品族都有相同的约束(在有多个业务品种、业务分类时,即:具有产品族&产品等级结构的概念),则可以使用抽象工厂模式

例如一个文本编辑器和一个图片处理器,都是软件实体,但是*nix下的文本编辑器和Windows下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片处理器也有类似情况。也就是具有了共同的约束条件:操作系统类型。于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。

源码地址:https://gitee.com/tianranll/java-design-patterns.git

参考文献:《设计模式之禅》、《大话设计模式》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77756.html

相关文章

  • 练就Java24章真经—你所不知道的工厂方法

    摘要:用专业的话来讲设计模式是一套被反复使用多数人知晓的经过分类编目的代码设计经验的总结创建型模式,共五种工厂方法模式抽象工厂模式单例模式建造者模式原型模式。工厂方法模式的扩展性非常优秀。工厂方法模式是典型的解耦框架。 前言 最近一直在Java方向奋斗《终于,我还是下决心学Java后台了》,今天抽空开始学习Java的设计模式了。计划有时间就去学习,你这么有时间,还不来一起上车吗? 之所以要学...

    Chiclaim 评论0 收藏0
  • 工厂模式理解了没有?

    摘要:我认为按书上的顺序比较好理解因为简单静态工厂模式是在工厂方法模式上缩减,抽象工厂模式是在工厂方法模式上再增强。所以我就先讲工厂方法模式了。抽象工厂模式抽象工厂模式就比较复杂了,我们一般的应用都写不到。 前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 昨天写了单例模式了,今天是时候写工厂模式啦~ 工厂模式我个人认为其实比...

    苏丹 评论0 收藏0
  • 设计模式之抽象工厂模式

    摘要:所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。 0x01.定义与类型 定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口 无需指定它们具体的类 类型:创建型 UML showImg(https://segmentfault.com/img/bVbtBp1?w=800&h=862...

    Acceml 评论0 收藏0
  • 我的Java设计模式-工厂方法模式

    摘要:工厂方法模式和策略的模式有什么区别区别在于,策略模式强调的是提供不同的策略方案,而工厂方法模式强调的是产品本身。 女朋友dodo闹脾气,气势汹汹的说我要吃雪糕。笔者心里啊乐滋滋的,一支雪糕就能哄回来,不亦乐乎?! 但是,雪糕买回来了,她竟然说不想吃雪糕了,突然想吃披萨。呵呵了,憋了一股劲没问题,又屁颠屁颠的去把披萨买回来。 世事难料,dodo同学又放大招了,披萨太上火了,我要吃芝士蛋糕...

    QiuyueZhong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<