【编程素质】设计模式-工厂模式(Factory Pattern)

1,工厂模式(Factory Pattern)

1)概念

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

2)意图

定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
工厂模式的本质是:对象创建对象使用进行解耦。

3)场景

解决的是客户端通过大量的判断来选择相应的对象,需要进行封装的问题。可以统一管理对象生命周期。

  1. Java 中的 Calendar、DateFormat 类;

4)产品等级结构(产品的继承结构)

5)产品族

在抽象工厂中,产品族指由同一个工厂生产的,位于不同产品等级结构中的一组产品。

6)优点

  1. 对象的创建和使用解耦
  2. 工厂方法有良好的扩展性。
  3. 屏蔽了产品类,调用者只需要关系产品类的接口。因为产品类的具体对象实例是由工厂产生的。

2,简单工厂(静态工厂,Simple Factory)

1)概念

利用静态方法定义一个简单的工厂。

2)场景

①工厂类负责创建的对象比较少。
②对象决定创建出哪一种产品类的实例,而对如何创建对象不关心。(例如你到肯德基说你要鸡腿,要薯条,要饮料还是,,,这时肯德基是一个工厂,客户端只需要点明自己要什么就行)。

  1. Spring中的BeanFactory:根据传入的唯一标识获得Bean对象。

3)优缺点

优:
不需要使用创建对象的方法来实例化对象。

缺:
①扩展性差
如果要我想增加一种形状,除了新增一个形状的具体类,还需要修改工厂类方法。
不能通过继承来改变创建方法的行为。
违反了高内聚责任分配原则。由于工厂类集中了所有实例的创建逻辑,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改
②不同的产品需要不同额外参数的时候不支持。

4)实现

①工厂类是一个具体的类(ShapeFactory ),非接口抽象类。有一个重要的create()方法(getShape),根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。利用if或者 switch创建产品并返回需要的实例。
②create()方法通常是静态的,所以也称之为静态工厂。

5)demo

UML
创建一个 Shape 接口,其子类实现工厂接口,返回的也是一个抽象的产品。
工厂类 ShapeFactory,通过使用ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。

①创建一个接口

public interface Shape {
   void draw();
}

②创建实现接口的实体类

public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}
public class Square implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}
public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

③创建一个工厂,生成基于给定信息的实体类的对

public class ShapeFactory {
    
   //使用 getShape 方法获取形状类型的对象
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
}

④使用该工厂,通过传递类型信息来获取实体类的对象

	  ShapeFactory shapeFactory = new ShapeFactory();

      //获取 Circle 的对象,并调用它的 draw 方法
      Shape shape1 = shapeFactory.getShape("CIRCLE");
      //调用 Circle 的 draw 方法
      shape1.draw();
      //获取 Rectangle 的对象,并调用它的 draw 方法
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
      //调用 Rectangle 的 draw 方法
      shape2.draw();
      //获取 Square 的对象,并调用它的 draw 方法
      Shape shape3 = shapeFactory.getShape("SQUARE");
      //调用 Square 的 draw 方法
      shape3.draw();

6)扩展性改良

①利用反射实现简单工厂

利用反射Class.forName(clz.getName()).newInstance()实现的简单工厂:

public class StaticNoodlesFactory {
    /**
     * 传入Class实例化面条产品类
     *
     * @param clz
     * @param <T>
     * @return
     */
    public static <T extends INoodles> T createNoodles(Class<T> clz) {
        T result = null;
        try {
            result = (T) Class.forName(clz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
//兰州拉面
        INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class);
        lz.desc();
        //泡面
        INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class);
        pm.desc();

优点:增加一种产品时,不需要修改create()的代码。
缺点:因为Class.forName(clz.getName()).newInstance()调用的是无参构造函数生成对象,它和new Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂。
不推荐,因为这样和简单的new一个对象一样,工厂方法应该用于复杂对象的初始化, 这样像为了工厂而工厂。

②Spring BeanPostProcessor

//基类接口 父类和子类都实现这个接口
public interface ShapeBaseInterface<T> {
}
//父类 
public abstract class AbstractShapeBaseService<T> implements ShapeBaseInterface {
	//此处可以用到模板方法
}
//子类
public class CircleShapeService<T> implements ShapeBaseInterface {
	//用模板方法 重写父类方法、实现父类抽象方法等
}
//工厂
public class ShapeFactory  implements BeanPostProcessor{
		
    /**
     * 具体的Shape类映射表 在spring启动,各容器注册之后将具体的Shape类如:CircleShapeService存储起来,方便调用。外部cotroller调用可以直接通过ShapeBaseInterface调用具体的方法,
     */
    private static Map<Integer, ShapeBaseInterface> SHAPE_MAP = new HashMap<>();
     /**
     * 注册Shape实例
     */
    public void register(Integer type, ShapeBaseInterface service) {
        SERVICE_MAP.put(type, service);
    }
     /**
     * 获取Spring接收容器中的所有实例
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ShapeBaseInterface ) {
            ShapeBaseInterface shapeService = (ShapeBaseInterface) bean;
            register(shapeService.getType(), shapeService);
            log.info("processor register service: type = {}, beanName = {}: {}", shapeService.getType(), beanName, bean.getClass().getName());
        }
        return bean;
    }
    //外部请求接口
    public static ShapeBaseInterface get(String type) {
        ShapeBaseInterface service = SHAPE_MAP.get(type);
        if (null != service){
            return service;
        }
        log.error("getType invalid type: {}", type);
        throw new GeneralException(ErrorEnum.TYPE_ERROR);
    }
}

3,工厂方法(Factory method)(推荐)

1)概念

定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个(因为创建者类不需要指定实际创建的产品是哪一个),工厂方法让类把实例化推迟到子类。
是常用的对象创建型设计模式,核心是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。

2)场景

①有一大堆不同的Pizza类,只有到了运行时,才能指定优化哪一个。这些是变化的把部分,若菜单改变,这部分代码也要改变。此时,提取出善变的这部分为独立类。

  1. 实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是:spring在调用getBean获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getObject。

3)优缺点

优:
将产品的“实现”从“使用”中解耦。
完全实现“开-闭 原则”,实现了可扩展

4)demo

它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品
UML

public class Main {

	public static void main(String[] args){
		/**
		 * 工厂模式
		 * 根据FoctoryType:"one""two"不同,让类把实例化推迟到子类
		 */
		Factory factory = new OneFactory();
		Product product = factory.FactoryMethod("one");
		System.out.println(product.productName);
		
		product = factory.FactoryMethod("two");
		System.out.println(product.productName);		
	}
}
/**
 * 工厂抽象类
 * 创建一个框架,让子类决定如何实现具体的产品
 * @author luo
 *
 */
public abstract class Factory {

	public Product FactoryMethod(String productType){
		Product product = CreateProduct(productType);
		return product;		
	}
	/**
	 * 让子类去实现要生产什么产品
	 */
	public abstract Product CreateProduct(String productType);
}
/**
 * 产品抽象类:
 * 所有产品都有实现这个接口,这样使用这些产品的类久可以引用这个类了
 * @author luo
 *
 */
public abstract class Product {
	public String productName;
}
/**
 * 具体产品类
 * @author luo
 *
 */
public class OneProduct extends Product{

	public OneProduct(){
		productName = "oneProduct";
	}
}
/**
 * 具体产品类
 * @author luo
 *
 */
public class TwoProduct extends Product{

	public TwoProduct(){
		productName = "twoProduct";
	}
}
/**
 * 具体工厂类
 * @author luo
 *
 */
public class OneFactory extends Factory{

	@Override
	public Product CreateProduct(String productType) {

		switch(productType){
		
		case "one":
			return new OneProduct();
		case "two":
			return new TwoProduct();
		default:
			break;
		}
		return null;
	}	
}

5)java中的应用

①java.util.concurrent.Executors类

java.util.concurrent.Executors类便是一个生成Executor 的工厂 ,其采用的便是 多方法静态工厂模式:
例如ThreadPoolExecutor类构造方法有5个参数,其中三个参数写法固定,前两个参数可配置,如下写:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

又如JDK想增加创建ForkJoinPool类的方法了,只想配置parallelism参数,便在类里增加一个如下的方法:

public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

4,抽象工厂

1)概念

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
这个接口的每个方法都负责创建一个具体产品,同时我们利用抽象工厂的子类来提供这些具体的做法,利用工厂方法来实现具体工厂。

2)场景

一个对象族(或者是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式

例如:
①一个应用需要在三个不同平台上运行,则可以通过抽象工厂模式屏蔽掉操作系统对应用的影响,由不同的产品类去处理与操作系统交互的信息。

3)优缺点

优:
①它分离了具体的类
②它使得易于交换产品系列
③它有利于产品的一致性
缺:
①难以支持新种类的产品
将工厂也抽象后,可能会有类爆炸问题。
如每次拓展新产品种类,不仅卖吃卖喝,我还想卖睡,提供床位服务,这需要修改抽象工厂类,因此所有的具体工厂子类,都被牵连,需要同步被修改。

4)demo

以上介绍的工厂都是单产品系的。抽象工厂是多产品系 (貌似也有产品家族的说法)。
举个例子来说,每个店(工厂)不仅仅卖面条,还提供饮料卖。

提供饮料卖,饮料是产品,先抽象一个产品类,饮料:

public abstract class IDrinks {
    /**
     * 描述每种饮料多少钱
     */
    public abstract void prices();
}

然后实现两个具体产品类: 可乐和水。

public class ColaDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("可乐三块五");
    }
}
public class WaterDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("和我一样的穷鬼都喝水,不要钱~!");
    }
}

抽象饭店,无外乎吃喝(抽象工厂类):

public abstract class AbstractFoodFactory {
    /**
     * 生产面条
     *
     * @return
     */
    public abstract INoodles createNoodles();

    /**
     * 生产饮料
     */
    public abstract IDrinks createDrinks();
}

具体工厂类:兰州大酒店、KFC。

public class LzlmFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new LzNoodles();//卖兰州拉面
    }

    @Override
    public IDrinks createDrinks() {
        return new WaterDrinks();//卖水
    }
}
public class KFCFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new PaoNoodles();//KFC居然卖泡面
    }

    @Override
    public IDrinks createDrinks() {
        return new ColaDrinks();//卖可乐
    }
}

使用:

		AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();

        abstractFoodFactory1= new LzlmFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();

5,简单工厂、工厂方法、抽象工厂区别

简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)
这里写图片描述

6,控制反转(IoC)与依赖注入(DI)

两者意思相近。是工厂模式与反射机制的综合应用。
把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。
IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF中。
IoC(控制反转:Inverse of Control)是Spring容器的内核,AOP、声明式事务等功能在此基础上开花结果。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值