1,工厂模式(Factory Pattern)
1)概念
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
2)意图
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
工厂模式的本质是:对象创建和对象使用进行解耦。
3)场景
解决的是客户端通过大量的判断来选择相应的对象,需要进行封装的问题。可以统一管理对象生命周期。
- Java 中的 Calendar、DateFormat 类;
4)产品等级结构(产品的继承结构)
5)产品族
在抽象工厂中,产品族指由同一个工厂生产的,位于不同产品等级结构中的一组产品。
6)优点
- 对象的创建和使用解耦
- 工厂方法有良好的扩展性。
- 屏蔽了产品类,调用者只需要关系产品类的接口。因为产品类的具体对象实例是由工厂产生的。
2,简单工厂(静态工厂,Simple Factory)
1)概念
利用静态方法定义一个简单的工厂。
2)场景
①工厂类负责创建的对象比较少。
②对象决定创建出哪一种产品类的实例,而对如何创建对象不关心。(例如你到肯德基说你要鸡腿,要薯条,要饮料还是,,,这时肯德基是一个工厂,客户端只需要点明自己要什么就行)。
- Spring中的BeanFactory:根据传入的唯一标识获得Bean对象。
3)优缺点
优:
不需要使用创建对象的方法来实例化对象。
缺:
①扩展性差
如果要我想增加一种形状,除了新增一个形状的具体类,还需要修改工厂类方法。
不能通过继承来改变创建方法的行为。
违反了高内聚责任分配原则。由于工厂类集中了所有实例的创建逻辑,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改
②不同的产品需要不同额外参数的时候不支持。
4)实现
①工厂类是一个具体的类(ShapeFactory ),非接口抽象类。有一个重要的create()方法(getShape),根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。利用if或者 switch创建产品并返回需要的实例。
②create()方法通常是静态的,所以也称之为静态工厂。
5)demo
创建一个 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类,只有到了运行时,才能指定优化哪一个。这些是变化的把部分,若菜单改变,这部分代码也要改变。此时,提取出善变的这部分为独立类。
- 实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是:spring在调用getBean获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getObject。
3)优缺点
优:
将产品的“实现”从“使用”中解耦。
完全实现“开-闭 原则”,实现了可扩展
4)demo
它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品
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、声明式事务等功能在此基础上开花结果。