YAZONG 我的开源

设计模式(二)创建型模式(对象创建型模式)简单工厂与工厂方法-FACTORY METHOD

 
0 评论0 浏览

2、2 工厂模式

定义

简单工厂

我们需要一个对象,会有一个工厂帮我们创建这个对象。

所有在用简单工厂的地方,都可以考虑用反射技术来取出 switch 或 if,减少分支判断带来的耦合。

简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。

该模式是工厂方法模式的弱化,因为简单,所以称为简单工厂模式,也叫做静态工厂模式。缺点是工厂类的扩展(不能通过继承来改变创建方法的行为)比较困难,不符合开闭原则,但它仍然是一个经常使用的设计模式。

工厂方法

定义一个用于创建对象的接口,让子类决定实例化哪一个类(作为一个生产的管理者,只要知道生产什么就可以了,而不需要事物的具体信息)。工厂方法使一个类的实例化延迟到其子类。

通用类图

image.png

Product:定义工厂方法所创建的对象的接口;

Creator:声明工厂方法,该方法返回一个 Product 类型的对象;

ConcreteProduct:具体的产品,实现了 Product 接口;

ConcreteCreator:重定义工厂方法返回一个 ConcreteProduct 实例。

使用场景

1.工厂方法模式一个 new 一个对象的替代品,所以在所有需要生产对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。

2.需要灵活地、可扩展的框架时。(比如三种网络协议:POP3、IMAP、HTTP)

3.工厂方法可以用在异构项目中。(比如从 WEBSERVICES 的 WSDL 中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。)

4.可以使用再测试驱动开发的框架下。(比如测试一个类 A,可以把与类 A 有关联关系的类 B 也同时产生/虚拟出来,避免类 A 与类 B 的耦合。现在弱化了,可用 JMock 和 EasyMock 代替。)

案例

案例 1(雷锋办好事)

初始设计
图形结构

image.png

代码示例
/**
* 现在只有一个大学生去帮敬老院去干活
*/

public class Gongchang1 {

   public static void main(String[] args) {

       LeiFeng leiFeng = new Undergraduate();

       leiFeng.sweep();

       leiFeng.wash();

       leiFeng.buy();

   }

}

class LeiFeng{

   public void sweep(){

       System.out.println("扫地");

   }

   public void wash(){

       System.out.println("洗衣服");

   }

   public void buy(){

       System.out.println("买东西");

   }

}



/**

* 学雷锋的大学生

*/

class Undergraduate extends LeiFeng{



}
简单工厂
图形结构

image.png

代码示例
/**

 * 现在不只有一个大学生去帮敬老院去干活,如果有多个大学生,或者有社区志愿者的话,那么可以编写简单工厂。

 */

public class Gongchang2 {

    public static void main(String[] args) {

        //在这里会发现重复了好多代码,出现了"坏味道",那么就可以通过Gongchang3工厂模式来改善这种问题。

        LeiFeng undergraduate1 = SimpleFactory.createLeiFeng("undergraduate");

        undergraduate1.sweep();

        undergraduate1.wash();

        undergraduate1.buy();

        System.out.println("------------------------------");

        LeiFeng undergraduate2 = SimpleFactory.createLeiFeng("undergraduate");

        LeiFeng undergraduate3 = SimpleFactory.createLeiFeng("undergraduate");

    }

}

class LeiFeng{

    public void sweep(){

        System.out.println("扫地");

    }

    public void wash(){

        System.out.println("洗衣服");

    }

    public void buy(){

        System.out.println("买东西");

    }

}

/**

 * 学雷锋的大学生

 */

class Undergraduate extends LeiFeng{

 

}

 

/**

 * 社区志愿者

 */

class Volunteer extends LeiFeng{

 

}

 

/**

 * 简单雷锋工厂

 */

class SimpleFactory{

    public static LeiFeng createLeiFeng(String type){

 

        LeiFeng leiFeng = null;

        switch (type){

            case "undergraduate":

                leiFeng = new Undergraduate();

                break;

            case "volunteer":

                leiFeng = new Volunteer();

                break;

        }

        return leiFeng;

    }

 

}
工厂方法
图形结构

image.png

代码示例
/**

 * 雷锋工厂方法

 * 这里如果把大学生换成社区志愿者,虽然也要修改代码,但是只需要修改一处(针对于Gongchang2中的switch和main而言)就可以了。

 * 所以Gongchang2和Gongchang3就能按场景来使用。

 * 那么可以发现工厂方法克服了简单工厂违背"开放-封闭原则"的缺点,又保持了封装对象"创建过程"的优点。

 * 但是简单工厂和工厂方法都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可以实现,降低了客户程序与产品对象的耦合。

 * 工厂方法模式是简单工厂模式的进一步抽象和推广。

 * 由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

 * 但缺点是由于每增加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。

 * 可是这并不是最佳的做法,这里并没有避免修改客服端的代码,在以后的章节中会通过"反射"机制来解决避免分支判断的问题。

 */

 

public class Gongchang3 {

    public static void main(String[] args) {

 

        IFactory undergraduateFactory = new UndergraduateFactory();

        undergraduateFactory.getLeifeng().sweep();

        undergraduateFactory.getLeifeng().wash();

        undergraduateFactory.getLeifeng().buy();

        System.out.println("------------------------------");

        IFactory volunterFactory = new VolunteerFactory();

 

    }

}

interface IFactory{

    LeiFeng getLeifeng();

}

class UndergraduateFactory implements IFactory{

    @Override

    public LeiFeng getLeifeng() {

        return new Undergraduate();

    }

}

class VolunteerFactory implements IFactory{

    @Override

    public LeiFeng getLeifeng() {

        return new Volunteer();

    }

}

interface LeiFeng{

    void sweep();

    void wash();

    void buy();

}

/**

 * 学雷锋的大学生

 */

class Undergraduate implements LeiFeng{

    public void sweep(){

        System.out.println("扫地");

    }

    public void wash(){

        System.out.println("洗衣服");

    }

    public void buy(){

        System.out.println("买东西");

    }

}

 

/**

 * 社区志愿者

 */

class Volunteer implements LeiFeng{

    public void sweep(){

        System.out.println("扫地");

    }

    public void wash(){

        System.out.println("洗衣服");

    }

    public void buy(){

        System.out.println("买东西");

    }

}

案例 2(计算器)

简单工厂
图形结构

image.png

代码示例
public class Gongchang4 {

    public static void main(String[] args) {

        Operation operation = OperationFactory.createOperate("+");;

        operation.getResult(1,2);

    }

}

class OperationFactory{

    public static Operation createOperate(String type){

        Operation operation = null;

 

        switch (type){

            case "+":

                operation = new OperationAdd();

                break;

            case "-":

                operation = new OperationSub();

                break;

        }

 

        return operation;

    }

}

interface Operation{

    void getResult(double num1,double num2);

}

class OperationAdd implements Operation{

    @Override

    public void getResult(double num1, double num2) {

        System.out.println("--add--" + (num1 + num2));

    }

}

class OperationSub implements Operation{

    @Override

    public void getResult(double num1, double num2) {

        System.out.println("--sub--" + (num1 - num2));

    }

}
工厂方法
图形结构

image.png

代码示例
public class Gongchang5 {

    public static void main(String[] args) {

        OperationFactory operationFactory = new OperationAddFactory();

        operationFactory.createOperate().getResult(2,1);

    }

}

interface OperationFactory{

    Operation createOperate();

}

class OperationAddFactory implements OperationFactory{

    @Override

    public Operation createOperate() {

        return new OperationAdd();

    }

}

class OperationSubFactory implements OperationFactory{

    @Override

    public Operation createOperate() {

        return new OperationSub();

    }

}

 

interface Operation{

    void getResult(double num1,double num2);

}

class OperationAdd implements Operation{

    @Override

    public void getResult(double num1, double num2) {

        System.out.println("--add--" + (num1 + num2));

    }

}

class OperationSub implements Operation{

    @Override

    public void getResult(double num1, double num2) {

        System.out.println("--sub--" + (num1 - num2));

    }

}

案例3(创建单例)

图形结构

image.png

代码示例
import java.lang.reflect.Constructor;

 

/**

 * 单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象。

 * 下述通过工厂方法模式创建了一个单例对象,该框架可以继续扩展,在一个项目中可以产生一个单例构造器,

 * 所有需要产生单例的类都遵循一定的规则(构造方法是private),然后通过扩展该框架,只要输入一个类型就可以获得唯一的一个实例。

 */

public class Gongchang6 {

}

 

/**

 * Singleton保证不能通过正常的渠道建立一个对象

 */

class Singleton{

    private Singleton(){}

    public void doSomething(){

 

    }

}

 

/**

 * SingletonFactory通过反射创建Singleton的单例对象

 * 在此案例中,通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存中的对象唯一。

 * 当然,其他类也可以通过反射的方式建立一个单例对象。

 */

class SingletonFactory{

 

    private static Singleton singleton;

 

    static{

        Class cl = null;

        try {

            cl = Class.forName(Singleton.class.getName());

            //获得无参构造

            Constructor constructor = cl.getDeclaredConstructor();

            //设置无参构造是可访问的

            constructor.setAccessible(true);

            //产生一个实例对象

            singleton = (Singleton)constructor.newInstance();

        } catch (Exception e) {

            e.printStackTrace();

        }

 

    }

 

    public static Singleton getSingleton(){

        return singleton;

    }

}

案例 4(延迟初始化/参数化工厂方法)

定义

一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用。

图形结构

image.png

代码示例
import java.util.HashMap;

import java.util.Map;

 

/**

 * 延迟加载框架时可以扩展的,例如限制某一个产品类的最大实例化数量,

 * 可以通过判断Map中已有的对象来实现,这样的处理是非常有意义的,

 * 例如JDBC数据库,都会要求设置一个MaxConnection最大连接数量,该数量就是内存中最大实例化的数量。

 * 延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,

 * 涉及多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性。

 */

 

public class Gongchang7 {

}

class ProductFactory{

    private static final Map<String,Product> prMap = new HashMap<String,Product>();

    public static synchronized Product createProduct(String type){

        Product product = null;

        if(prMap.containsKey(type)){

            product = prMap.get(type);

        }else{

            if(type.equals("Product1")){

                product = new ConcreteProduct1();

            }else{

                product = new ConcreteProduct2();

            }

        }

        return product;

    }

}

abstract class Product{

    abstract void doSomething();

}

class ConcreteProduct1 extends Product{

    @Override

    public void doSomething() {

 

    }

}

class ConcreteProduct2 extends Product{

    @Override

    public void doSomething() {

 

    }

}

优点

1.良好的封装性(能够封装具体类型的实例化),代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。

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

3.屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关系产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。因为产品类的实例化工厂是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。

4.工厂方法模式克服了简单工厂模式违背的“开放-封闭原则”的缺点,又保持了封装对象创建的过程的优点。

缺点

工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移动到了客户端代码来进行。如果要加功能,本来是改工厂类的,而现在是修改客户端。

工厂方法模式还给扩展性和可维护性带来了一定的影响。如果要扩展一个产品类,就需要建立一个相应的工厂类,遮掩更久增加了扩展的难度。因为工厂类和产品类的数量相同,维护时需要考虑两个对象之间的关系。

解决方法是:在复杂的应用中一般采用多工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。


标题:设计模式(二)创建型模式(对象创建型模式)简单工厂与工厂方法-FACTORY METHOD
作者:yazong
地址:https://blog.llyweb.com/articles/2020/04/16/1587051290502.html