设计模式-工厂模式

117 阅读6分钟

产生背景:我们需要对同一类型产生一组对应事物时,我们需要对其进行管理。比如我们要购买商品,我们可以到各个门店自行购买;当然我们也可以到一个专卖店,根据需求,它给我们提供各种款式的商品。

//商品接口
public interface IProduct {
 public void dosomething();
}
public class ProductA implements IProduct{
    @Override
    public void dosomething() {
        System.out.println("我是A商品");
    }
}
public class ProductB implements IProduct{
    @Override
    public void dosomething() {
        System.out.println("我是B商品");
    }
}

传统方式调用:

if(type.equals("A")){
IProduct  productA=new ProductA();
productA.dosomething();
}
if(type.equals("B")){
IProduct  productB=new ProductB();
  productB.dosomething();
}

传统方式我们可以得出结论:

优点:简单易理解 需要什么类直接new 然后调用其方法,假如有新类型商品进来继续创建对应的类,加入对应type的实现逻辑。

缺点:不符合开闭原则,每次加入新类型的商品都要修改调用逻辑,对象直接创建,不符合迪米特法则。

工厂模式定义: 在该模式中,工厂父类负责提供创建产品对象的公共接口,而工厂子类负责生成具体的产品对象,换而言之,调用方只需要知道产品的类名或者某个标识就可以了,不需要知道产品对象的详细创建过程,将具体类的实例化操作延迟到工厂子类中完成,降低模块之间的耦合性。提供代码结构的扩展性,屏蔽每个功能类中的具体实现逻辑,减少开发、维护的成本。

一、简单工厂模式

1.类图

image.png

2.代码:

public class SimpleProductFactory {

    public static IProduct createProduct(String type){
        IProduct product=null;
        if(type.equals("A")){
            product=new ProductA();
        }
        if(type.equals("B")){
            product=new ProductB();
        }
       return product;
    }

    public static IProduct createProductByClass(Class<IProduct> clazz) throws InstantiationException, IllegalAccessException {
        IProduct product = clazz.newInstance();
        return product;
    }
}

简单工厂调用:

IProduct product= SimpleProductFactory.createProduct(type);
product.dosomething();

//优点:创建对象逻辑由工厂实现,调用简单,调用方不需要修改太多代码,改变类型即可
//缺点:违反开闭原则,当有新来商品类型需要到工厂修改对应商品创建逻辑。

简单工厂改进:

//简单工厂模式优化
Class clazz=ProductB.class;
IProduct productByClass = SimpleProductFactory.createProductByClass(clazz);
productByClass.dosomething();

//优点:符合开闭原则 新来的商品类型通过反射生产对象,不需要修改工厂实现逻辑
//缺点:由于对象由反射创建,可控性差,容易产生bug

应用实例:

  • jdk源码:Calendar类

image.png

  • Logback中的LoggerFactory,根据入参不同存在多个重载的getLogger方法
public static Logger getLogger(String name) {
 ILoggerFactory iLoggerFactory = getILoggerFactory();
 return iLoggerFactory.getLogger(name);
}

public static Logger getLogger(Class<?> clazz) {
 Logger logger = getLogger(clazz.getName());
 if (DETECT_LOGGER_NAME_MISMATCH) {
     Class<?> autoComputedCallingClass = Util.getCallingClass();
     if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
         Util.report(String.format("Detected logger name mismatch. Given name: "%s"; computed name: "%s".", logger.getName(), autoComputedCallingClass.getName()));
         Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
     }
 }

 return logger;
}

二、工厂方法模式

简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则。

1.类图

image.png

2.代码

//增加顶级工厂类 不直接操作商品 改由其子类去操作商品类
public interface IFactory {

    public IProduct createProduct();
}

public class FactoryA implements IFactory{
    @Override
    public IProduct createProduct() {
        return new ProductA();
    }
}

public class FactoryB implements IFactory{
    @Override
    public IProduct createProduct() {
        return new ProductB();
    }
}

结论:

工厂方法模式:指定义一个创建对象的接口,但由实现这个接口的类来决定实例化哪个类,工厂方法把类的实例化推迟到子类中进行。 也就是说,工厂方法模式中,不在由工厂类生产对应的产品,而是由工厂类的子类实现具体产品的生产逻辑,解决简单工厂中switch case的臃肿,也符合开闭原则。

厂方法模式主要是解决产品的扩展问题,当需要增加新产品时,我们只需要新增类去实现工厂接口,做到工厂职责的单一化,符合单一职责原则。

缺点:用户与具体工厂子类高度耦合,比如用户购买A商品必须到A工厂,增加了用户的操作复杂性。

应用:

  • 工厂方法模式在Logback应用
public interface ILoggerFactory {
    Logger getLogger(String var1);
}
public class NOPLoggerFactory implements ILoggerFactory {
    public NOPLoggerFactory() {
    }

    public Logger getLogger(String name) {
        return NOPLogger.NOP_LOGGER;
    }
}

三、抽象工厂模式

为了解决工厂方法的问题,我们可以把工厂类抽象为接口,用户只需要去找默认的工厂提出自己的需求(传入参数),便能得到自己想要产品,而不用根据产品去寻找不同的工厂,方便用户操作。这也就是我们接下来要说的抽象工厂模式。

1.类图:

image.png

2.代码:

public interface IProduct {
    public void dosomething();
}


class ProductA1 implements IProduct{
    @Override
    public void dosomething() {
        System.out.println("我是A1");
    }
}


class ProductA2 implements IProduct{
    @Override
    public void dosomething() {
        System.out.println("我是A2");
    }
}

class ProductB1 implements IProduct{
    @Override
    public void dosomething() {
        System.out.println("我是B1");
    }
}

class ProductB2 implements IProduct{
    @Override
    public void dosomething() {
        System.out.println("我是B2");
    }
}
public interface IAbstractFactory {
    public IProduct createProduct1();
    public IProduct createProduct2();
}
public class FactoryA implements IAbstractFactory{

    public FactoryA(){
        System.out.println("----来自工厂A的----");
    }

    @Override
    public IProduct createProduct1() {
        return new ProductA1();
    }

    @Override
    public IProduct createProduct2() {
        return new ProductA2();
    }
}
public class FactoryB implements IAbstractFactory{

    public FactoryB(){
        System.out.println("----来自工厂B的----");
    }
    @Override
    public IProduct createProduct1() {
        return new ProductB1();
    }

    @Override
    public IProduct createProduct2() {
        return new ProductB1();
    }
}
public class Main {
    public static void main(String[] args) {
        IAbstractFactory factory=new FactoryA();
        factory.createProduct1().dosomething();
        factory.createProduct2().dosomething();

        IAbstractFactory factory2=new FactoryB();
        factory2.createProduct1().dosomething();
        factory2.createProduct2().dosomething();
    }
}

结论:

抽象工厂模式(Abstract Factory Pattern)指提供一个创建一系列相关或相互依赖对象的接口,无需指定他们具体的类。客户端不需要指定产品的具体类型,创建多个产品族中的产品对象。

抽象工厂模式适用于需要生成产品族的情景。抽象产品类内部提供了多个其他抽象产品,抽象工厂提供了多个具体的工厂子类,可以生产相应的产品族对象。

应用:

  • 抽象工厂在Spring中的使用(BeanFactory)

在Spring中,所有工厂都是BeanFactory的子类,BeanFactory根据不同的策略调用getBean,获取不同的对象。

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

总结:

简单工厂模式就是建立一个实例化对象的类,在该类中对多个对象实例化。工厂方法模式是定义了一个创建对象的抽象方法,由子类决定要实例化的类。这样做的好处是再有新的类型的对象需要实例化只要增加子类即可。抽象工厂模式定义了一个接口用于创建对象族,而无需明确指定具体类。抽象工厂也是把对象的实例化交给了子类,即支持拓展。同时提供给客户端接口,避免了用户直接操作子类工厂。

工厂模式提供了一种创建对象的机制,抽象实例化的过程,隐藏了对象的创建细节,对外只提供一个通用接口,能够提升已有代码的灵活性和可复性。创建型模式有五种:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。