Java设计模式之工厂方法和抽象工厂
全网最详细的工厂设计模式,本文主要是创建型设计模式中的工厂方法和抽象工厂,先由传统实现方式引出问题,接着对代码改进到简单工厂,后扩展到工厂方法,最后是抽象工厂模式,文中包括概念理解和相关实现代码。
读者可以拉取完整代码本地学习,实现代码均测试通过上传到码云
一、引出问题
如果有一个客户老王,需要购买产品,产品分别是A、B、C。
如果用传统方法实现,分别定义A、B、C三个类,再分别创建他们所属的方法。
在客户对象中再分别调用他们的方法。
Product ClientProduct(String orderType) { Product product; if (orderType.equals("A")) { product = new ProductA(); } else if (orderType.equals("B")) { product = new ProductB(); } else if (orderType.equals("B")) { product = new ProductC(); } // product制作过程 product.common(); return product; }
如果我们需要再增加一个产品D,就需要判断再增加一个分支,然后在分支里面创建产品对象,调用产品D的方法。
这样代码的维护性是极差的,查看我们的软件设计七大原则,很明显,这违反了开闭原则、依赖倒置原则.
如果又有个客户小王,那么小王也必须依赖每个产品类,这样就显得冗杂。
二、简单工厂(静态工厂)
简单工厂(静态工厂)模式就应用而生了。
如果我们将产品A、B、C抽象出来一个父类,再专门创建一个工厂类,在客户购买产品时,只需要传入产品的类型,由工厂去创建产品。
简单工厂模式中包含如下角色:
Factory:工厂角色
工厂角色负责实现创建所有实例的内部逻辑。
Product:抽象产品角色
抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口。
ConcreteProduct:具体产品角色
具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
看看我们对原始实现方式后的代码。
产品抽象父类:
/** * @author tcy * @Date 28-07-2022 */ public class Product { public void common(){ System.out.println("这是产品父类公共方法..."); } }
产品A:
/** * @author tcy * @Date 28-07-2022 */ public class ProductA extends Product{ public void common(){ System.out.println("这是产品A方法..."); } }
产品B:
/** * @author tcy * @Date 28-07-2022 */ public class ProductB extends Product{ public void common(){ System.out.println("这是产品B方法..."); } }
工厂类:
/** * @author tcy * @Date 28-07-2022 */ public class SimpleFactory { public Product createProduct(String orderType) { Product product = null; if (orderType.equals("A")) { product = new ProductA(); } else if (orderType.equals("B")) { product = new ProductB(); } else if (orderType.equals("C")) { product = new ProductC(); } return product; } }
客户老王类:
/** * @author tcy * @Date 28-07-2022 */ public class Client { SimpleFactory simpleFactory; public Client(SimpleFactory simpleFactory) { this.simpleFactory = simpleFactory; } public Product orderProduct(String orderType) { Product product; product = simpleFactory.createProduct(orderType); //调用每个产出相应的方法 product.common(); System.out.println(product.getClass()); return product; } public static void main(String[] args) { Client client=new Client(new SimpleFactory()); client.orderProduct("A"); } }
这样简单工厂模式就实现了,这样的话老王和具体的产品就很好的解耦了,也不需要老王再依赖具体产品类,依赖倒置问题就很好的解决了。
如果增加一个产品D,需要再重新定义一个D类,实现product接口,而后在工厂类中增加一个分支结构。
显而易见这样实现,缺陷依然存在:
1、工厂类集中了所有产品创建逻辑,职责过重,一旦发生异常,整个系统将受影响。
2、使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
3、系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
4、简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
这种方法只是一种编码方式,并不输入设计模式的一种,且局限于产品种类较少。
三、工厂方法
在简单工厂中老王需要具体的产品,就在他自己的类中去创建需要的产品,老王虽然不依赖具体产品,但老王现在需要依赖工厂实现类了。
简单工厂是将产品类抽象化,具体的产品由工厂类去实现。
如果我们将工厂类也抽象化,那就引出了我们今天第一个设计模式——工厂方法。
工厂方法有四个角色:
1、抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 createProduct() 来创建产品。
2、具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
3、抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
4、具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
我们对简单工厂代码进行改造。
抽象产品父类、产品A类、产品B类保持不变。重点看工厂类
抽象工厂类:
/** * @author tcy * @Date 28-07-2022 */ public abstract class AbstractFactory { public abstract Product createProduct(String orderType); }
具体实现工厂A类:
/** * @author tcy * @Date 28-07-2022 */ public class ConcreteFactoryA extends AbstractFactory{ @Override public Product createProduct(String orderType) { System.out.println("参数为:"+orderType); return new ProductA(); } }
具体实现工厂B类:
/** * @author tcy * @Date 28-07-2022 */ public class ConcreteFactoryB extends AbstractFactory{ @Override public Product createProduct(String orderType) { return new ProductB(); } }
老王类:
/** * @author tcy * @Date 28-07-2022 */ public class Client { AbstractFactory simpleFactory; public Client(AbstractFactory simpleFactory) { this.simpleFactory = simpleFactory; } public Product orderProduct(String orderType) { Product product; product = simpleFactory.createProduct(orderType); //调用每个产出相应的方法 product.common(); System.out.println(product.getClass()); return product; } public static void main(String[] args) { Client client=new Client(new ConcreteFactoryA()); client.orderProduct("A"); } }
这样的好处就在于老王只管他要关注的抽象工厂,具体是哪个工厂实现类生产产品,老王也不需要关注。
典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点也是显而易见的:
- 类的个数容易过多,增加复杂度。
- 考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
- 抽象产品只能生产一种产品。
如果对工厂方法依然一知半解的话,接着往下看工厂方法的一个典型实现
在JDK中对工厂方法有大量的运用,其中比较典型的是
new ArrayList<>().iterator();
我们知道集合的一个大分支依赖的是Collection接口,我们以ArrayList作为举例。
Collection接口相当于产品的抽象父类,ArrayList相当于具体产品。
Iterator是一个抽象工厂,在ArrayList中有一个Itr内部类实现了Iterator接口
当我们调用集合的iterator()方法遍历对象时,就会调用各自类的具体实现方法。
四、抽象工厂
有一天,产品A、B、C升级改造了,三种产品分别有红色和蓝色,如果还用工厂方法的话,那简直是个灾难,具体工厂实现类需要六个。
就引出我们今天的第二个设计模式——抽象工厂。
抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建创建一系列相关或相互依赖对象的家族,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
抽象工厂模式与工厂方法模式区别在于,工厂方法模式针对的是一个产品等级结构。
而抽象工厂模式则需要面对多个产品等级结构(各种颜色),一个工厂等级结构可以负责多个不同产品等级结构(不同颜色)中的产品对象的创建 。
抽象工厂依然是四个角色:
- AbstractFactory:抽象工厂
- ConcreteFactory:具体工厂
- AbstractProduct:抽象产品
- Product:具体产品
我们开始改造工厂方法代码,既然是要把产品都分成一组,那理应把产品A、B、C都抽象出来,再让工厂类去实现各个产品的不同颜色,也就是概念中的——用于创建创建一系列相关或相互依赖对象的家族。
接口看改造后的代码:
产品抽象类:
/** * @author tcy * @Date 28-07-2022 */ public interface Product { public void common(); }
产品抽象A家族类:
/** * @author tcy * @Date 28-07-2022 */ public abstract class ProductA implements Product { public abstract void common(); }
产品抽象B家族类:
/** * @author tcy * @Date 28-07-2022 */ public abstract class ProductB implements Product { public abstract void common(); }
具体红色产品A类:
/** * @author tcy * @Date 28-07-2022 */ public class RedProductA extends ProductA{ @Override public void common() { System.out.println("这是红色的产品A"); } }
具体蓝色产品A类:
/** * @author tcy * @Date 28-07-2022 */ public class BlueProductA extends ProductA { @Override public void common() { System.out.println("这是蓝色的产品A"); } }
抽象A家族工厂类:
/** * @author tcy * @Date 28-07-2022 */ public interface AbstractProductFactory { public ProductA createProduct(String orderType); }
实现A家族工厂类:
/** * @author tcy * @Date 28-07-2022 */ public class ConcreateProductAFactory implements AbstractProductFactory{ @Override public ProductA createProduct(String orderType) { return new BlueProductA(); } }
老王类:
/** * @author tcy * @Date 28-07-2022 */ public class Client { ConcreateProductAFactory simpleFactory; public Client(ConcreateProductAFactory simpleFactory) { this.simpleFactory = simpleFactory; } public ProductA orderProduct(String orderType) { ProductA product; product = simpleFactory.createProduct(orderType); //调用每个产出相应的方法 product.common(); System.out.println(product.getClass()); return product; } public static void main(String[] args) { Client client=new Client(new ConcreateProductAFactory()); client.orderProduct("A"); } }
这样的话,每天工厂类可以把A的产品家族类(红、蓝)都实现,抽象工厂模式隔离了具体类的生成,使得老王并不需要知道什么产品被创建,从具体的产品解耦出来。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
如果要增加新产品、和新工厂很容易,如果再增加一个等级(颜色)代码修改起来就很痛苦了。
抽象工厂模式在Spring中有大量的运用。
比较典型的是,BeanFactory 是用于管理 Bean 的一个工厂,所有工厂都是 BeanFactory 的子类。这样我们可以通过 IOC 容器来管理访问 Bean,根据不同的策略调用 getBean() 方法,从而获得具体对象。
BeanFactory 的子类主要有
ClassPathXmlApplicationContext、
XmlWebApplicationContext、
StaticWebApplicationContext、
StaticApplicationContext。
在 Spring 中,DefaultListableBeanFactory 实现了所有工厂的公共逻辑。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接