设计模式之禅-工厂模式

工厂方法模式

定义一个用于创建对象的 接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
在这里插入图片描述

/**
 * 抽象产品类
 *
 * @author gcl
 * @date 2020-08-13 0:06
 */
public abstract class Product {
    // 产品类的公共方法
    public void method1() {
        //业务处理逻辑
    }

    // 抽象方法
    public abstract void method2();
}
/**
 * 抽象工厂类
 *
 * @author gcl
 * @date 2020-08-13 0:09
 */
public abstract class Creator {
    /**
     * 创建一个产品对象,输入参数可自行设置
     *
     * @param c class
     * @param <T> Product接口的子类
     * @return 具体产品对象
     */
    public abstract <T extends Product> T createProduct(Class<T> c);
}
/**
 * 具体工厂类
 *
 * @author gcl
 * @date 2020-08-13 0:11
 */
public class ConcreteCreator extends Creator {
    @Override
    public <T extends Product> T createProduct(Class<T> c) {
        Product product = null;
        try {
            product = (Product) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {

        }
        return (T) product;
    }
}
/**
 * 具体产品类
 *
 * @author gcl
 * @date 2020-08-13 0:08
 */
public class ConcreteProduct1 extends Product {

    @Override
    public void method2() {

    }
}
/**
 * 具体产品类
 *
 * @author gcl
 * @date 2020-08-13 0:08
 */
public class ConcreteProduct2 extends Product {

    @Override
    public void method2() {

    }
}
/**
 * 场景类
 *
 * @author gcl
 * @date 2020-08-13 0:13
 */
public class Client {
    public static void main(String[] args) {
        ConcreteCreator creator = new ConcreteCreator();
        ConcreteProduct1 product = creator.createProduct(ConcreteProduct1.class);
        //业务处理
    }
}

优点

  • 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需 要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创 建对象的艰辛过程,降低模块间的耦合。
  • 工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体 的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。
  • 屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关 心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。
  • 工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实 现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依 赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题!

使用场景

  • 工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以 使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
  • 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
  • 工厂方法模式可以用在异构项目中
  • 可以使用在测试驱动开发的框架下。例如,测试一个类A,就需要把与类A有关 联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B 的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了。

工厂方法模式的扩展

缩小为简单工厂模式

将工厂类的方法定义为static方法。

升级为多个工厂类

每一个产品类都对应了一个工厂类,好处就是创建类的职责清晰,而且结构简单,但是给可扩展性和可维护性带来了一定的影响。

替代单例模式
public class Singleton {
    private Singleton(){}
    public void doSomething(){}
}
/**
 * 负责生成单例的工厂类<p>
 * 通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存中的对象唯一
 * @author gcl
 * @date 2020-08-13 0:42
 */
public class SingletonFactory {
    private static Singleton singleton;

    static {
        try {
            Class clazz = Class.forName(Singleton.class.getName());
            Constructor constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            singleton = (Singleton) constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Singleton getSingleton() {
        return singleton;
    }
}
延迟初始化

一个对象被消费完毕后,并不立刻释放,工厂类 保持其初始状态,等待再次被使用。
在这里插入图片描述
ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要 再次被重用的对象保留。例如spring的IOC容器,Mybatis的一级缓存,都是使用类似的方式。

/**
 *
 * 延迟加载的工厂类 <p>
 * 代码还比较简单,通过定义一个Map容器,容纳所有产生的对象,如果在Map容器中已经有的对象,则直接取出返回;如果没有,<p>
 * 则根据需要的类型产生一个对象并放入到Map容器中,以方便下次调用。<p>
 * @author gcl
 * @date 2020-08-14 21:28
 */
public class LazyInitFactory {
    private static final Map<String, Product> PRODUCT_MAP = new HashMap<>();
    public static synchronized Product createProduct(String type){
        Product product = null;
        if (PRODUCT_MAP.containsKey(type)){
            product = PRODUCT_MAP.get(type);
        }else{
            if ("Product1".equals(type)){
                product = new ConcreteProduct1();
            }else{
                product = new ConcreteProduct2();
            }
            PRODUCT_MAP.put(type,product);
        }
        return product;
    }
}

抽象工厂模式

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们 的具体类。
在这里插入图片描述
抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象 工厂模式产生需要的对象是一种非常好的解决方式。
在这里插入图片描述

public abstract class AbstractCreator {
    /**
     * A产品家族
     *
     * @return AbstractProductA
     */
    public abstract AbstractProductA createProductA();

    /**
     * B产品家族
     *
     * @return AbstractProductB
     */
    public abstract AbstractProductB createProductB();
}

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

public abstract class AbstractProductA {
    /**
     * 产品共有方法
     */
    public void shareMethod() {
    }

    /**
     * 每个产品相同方法,不同实现
     */
    public abstract void doSomething();
}
public abstract class AbstractProductB {
    /**
     * 产品共有方法
     */
    public void shareMethod() {
    }

    /**
     * 每个产品相同方法,不同实现
     */
    public abstract void doSomething();
}
public class Creator1 extends AbstractCreator {
    @Override
    public AbstractProductA createProductA() {
        return new ProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ProductB1();
    }
}
public class Creator2 extends AbstractCreator {
    @Override
    public AbstractProductA createProductA() {
        return new ProductA2();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ProductB2();
    }
}
public class ProductA1 extends AbstractProductA {
    @Override
    public void doSomething() {
        System.out.println("product A1 implement");
    }
}
public class ProductA2 extends AbstractProductA {
    @Override
    public void doSomething() {
        System.out.println("product A2 implement");
    }
}
public class ProductB1 extends AbstractProductB {
    @Override
    public void doSomething() {
        System.out.println("product B1 implement");
    }
}
public class ProductB2 extends AbstractProductB {
    @Override
    public void doSomething() {
        System.out.println("product B2 implement");
    }
}

注意:有M个产品等级就应该有M个实现工厂类,在每个实现工厂中,实现不同产品族 的生产任务。

/**
 * 一个模式在什么情况下才能够使用,是很多读者比较困惑的地方。</br>
 * 抽象工厂模式是一个简单的模式,使用的场景非常多,大家在软件产品开发过程中,涉及不同操作系统的时候,
 * 都可以考虑使用抽象工厂模式,例如一个应用,需要在三个不同平台(Windows、Linux、Android(Google发布的智能终端操作系统))上运行,
 * 你会怎么设计?分别设计三套不同的应用?非也,通过抽象工厂模式屏蔽掉操作系统对应用的影响。
 * 三个不同操作系统上的软件功能、应用逻辑、UI都应该是非常类似的,唯一不同的是调用不同的工厂方法,
 * 由不同的产品类去处理与操作系统交互的信息。
 * @author gcl
 * @date 2020-09-21 15:51
 */
public class Client {
    public static void main(String[] args) {
        // 两个工厂
        AbstractCreator creator1 = new Creator1();
        AbstractCreator creator2 = new Creator2();
        // 产生A1
        AbstractProductA productA1 = creator1.createProductA();
        // 产生B1
        AbstractProductB productB1 = creator1.createProductB();
        // 产生A2
        AbstractProductA productA2 = creator2.createProductA();
        // 产生B2
        AbstractProductB productB2 = creator2.createProductB();
        // 业务代码

    }
}

在场景类中,没有任何一个方法与实现类有关系,对于一个产品来说,我们只要知道它 的工厂方法就可以直接产生一个产品对象,无须关心它的实现类。

优点

  • 封装性,每个产品的实现类不是高层模块要关心的,它要关心的是接口,是抽象,它不关心对象是如何创建出来。由工厂类负责,只要知道工厂类是谁,就能创建出一个需要的对象,省时省力,优秀设计就应该如此。
  • 产品族内的约束为非公开状态。

缺点

抽象工厂模式的最大缺点就是产品族扩展非常困难。我们以通用代码 为例,如果要增加一个产品C,也就是说产品家族由原来的2个增加到3个,看看我们的程序有多大改动吧!抽象类AbstractCreator要增加一个方法createProductC(),然后两个实现类都要修改。严重违反了开闭原则,而且我们一直说明抽象类和接口是一个契约。改变契约,所有与契约有关系的代码都要修改。

使用场景

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

注意事项

在抽象工厂模式的缺点中,我们提到抽象工厂模式的产品族扩展比较困难,但是一定要清楚,是产品族扩展困难,而不是产品等级。在该模式下,产品等级是非常容易扩展的,增加一个产品等级,只要增加一个工厂类负责新增加出来的产品生产任务即可。也就是说横向扩展容易,纵向扩展困难。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值