当前位置: 首页 > 后端技术 > Java

设计模式之工厂方法与抽象工厂

时间:2023-04-02 01:35:27 Java

全网最详尽的工厂设计模式。本文主要关注创建型设计模式中的工厂方法和抽象工厂。先是传统的实现方式导致问题,然后代码改进为简单工厂,再扩展为工厂方法,最后是抽象工厂模式,包括概念理解和相关实现代码。读者可以拉取完整代码在本地学习,实现代码全部测试上传到码云。如果用传统方法实现,分别定义A、B、C三个类,然后创建它们所属的方法。在客户端对象中分别调用它们的方法。ProductClientProduct(StringorderType){Product产品;if(orderType.equals("A")){product=newProductA();}elseif(orderType.equals("B")){product=newProductB();}elseif(orderType.equals("B")){product=newProductC();}//产品生产流程product.common();returnproduct;}如果我们需要再添加一个产品D,需要判断添加一个分支,然后在分支中创建一个产品对象,调用产品D的方法,这样的代码可维护性极差。看我们的软件设计七大原则,很明显这违背了开闭原则和依赖倒置原则。如果再有一个客户小王,那么小王也必须依赖每个产品类,这就变得多余了。2.简单工厂(静态工厂)简单工厂(静态工厂)模式应用诞生。如果我们把产品A、B、C抽象成一个父类,然后创建一个工厂类,客户购买产品时,只需要传入产品类型,工厂就会创建产品。简单工厂模式包含以下角色:Factory:工厂角色工厂角色负责实现创建所有实例的内部逻辑。Product:抽象产品角色抽象产品角色是创建的所有对象的父类,负责描述所有实例共享的公共接口。ConcreteProduct:具体产品角色具体产品角色是创建对象,所有创建的对象都作为该角色的具体类的实例。看看我们最初实现背后的代码。产品抽象父类:/***@authortcy*@Date28-07-2022*/publicclassProduct{publicvoidcommon(){System.out.println("这是产品父类的公共方法...");}}ProductA:/***@authortcy*@Date28-07-2022*/publicclassProductAextendsProduct{publicvoidcommon(){System.out.println("这是ProductA的方法..”);}}产品B:/***@authortcy*@Date28-07-2022*/publicclassProductBextendsProduct{publicvoidcommon(){System.out.println("这是产品B的方法...");}}工厂类:/***@authortcy*@Date28-07-2022*/publicclassSimpleFactory{publicProductcreateProduct(StringorderType){Productproduct=null;if(orderType.equals("A")){product=newProductA();}elseif(orderType.equals("B")){product=newProductB();}elseif(orderType.equals("C")){product=newProductC();}返回产品;}}客户法老类:/***@authortcy*@Date28-07-2022*/publicclassClient{SimpleFactorysimpleFactory;publicClient(SimpleFactorysimpleFactory){this.simpleFactory=simpleFactory;}publicProductorderProduct(StringorderType){Product产品;product=simpleFactoryTypeder.createProduct);product(或者//为每个输出调用相应的方法product.common();System.out.println(product.getClass());returnproduct;}publicstaticvoidmain(String[]args){Clientclient=newClient(newSimpleFactory());client.orderProduct("A");}}这个简单工厂模型就实现了,这样就很好的解耦了老王和具体的产品,不需要老王去依赖具体的products.class,依赖倒置问题就很好解决了,如果添加了一个productD,需要重新定义一个D类来实现product的接口,然后在工厂类中添加一个分支结构。显然,这种实现方式仍然存在缺陷:1、工厂类集中了所有的产品创建逻辑,责任太重,一旦出现异常,将影响整个系统。2、使用简单工厂模式会增加系统中的类,增加了在某个程序中理解系统的复杂性和难度。3、系统扩展困难。添加新产品后,必须修改工厂逻辑。当产品类型很多时,逻辑可能会过于复杂。4、由于简单工厂模型中使用了静态工厂方法,工厂角色无法形成基于继承的层次结构。这种方法只是一种编码方法,不是输入设计模式的一种,仅限于较少种类的产品。3.工厂方法在简单工厂中,老王需要特定的产品,所以他在自己的类中创建所需的产品。虽然老王不依赖具体的产品,但是老王现在需要依赖工厂来实现类。简单工厂是对产品类进行抽象,具体的产品由工厂类实现。如果我们把工厂类也抽象出来,就引出了我们今天的第一个设计模式——工厂方法。工厂方法有四种作用:1.抽象工厂(AbstractFactory):提供创建产品的接口,调用者通过它访问具体工厂的工厂方法createProduct()来创建产品。2、ConcreteFactory:主要实现抽象工厂中的抽象方法,完成具体产品的创建。3、抽象产品(Product):定义了产品的规格,描述了产品的主要特性和功能。4、具体产品:实现抽象产品角色定义的接口,由具体工厂创建,与具体工厂一一对应。我们改造简单工厂代码。抽象产品父类、产品A类、产品B类保持不变。关注工厂类抽象工厂类:/***@authortcy*@Date28-07-2022*/publicabstractclassAbstractFactory{publicabstractProductcreateProduct(StringorderType);}工厂类A的具体实现:/***@authortcy*@Date28-07-2022*/publicclassConcreteFactoryAextendsAbstractFactory{@OverridepublicProductcreateProduct(StringorderType){System.out.println("参数为:"+orderType);返回新的ProductA();}}工厂类B的具体实现:/***@authortcy*@Date28-07-2022*/publicclassConcreteFactoryBextendsAbstractFactory{@OverridepublicProductcreateProduct(StringorderType){returnnewProductB();}}老王类:/***@authortcy*@Date28-07-2022*/publicclassClient{AbstractFactorysimpleFactory;publicClient(AbstractFactorysimpleFactory){this.simpleFactory=simpleFactory;}publicProductorderProduct(StringorderType){Product产品;product=simpleFactory.createProduct(orderType);//为每个输出调用相应的方法产品.common();System.out.println(product.getClass());退回产品;}publicstaticvoidmain(String[]args){Clientclient=newClient(newConcreteFactoryA());client.orderProduct("A");}}这样做的好处是老王只关心他要关注的抽象工厂,哪个工厂实现产品的生产,老王不需要关注典型的解耦框架。高层模块只需要知道产品的抽象类,不需要关心其他的实现类,满足迪米特定律、依赖倒置原则和里氏替换原则。缺点也很明显:类的数量容易过多,增加了复杂度。考虑到系统的可扩展性,需要引入一个抽象层,定义在客户端代码中,增加了系统的抽象度和理解难度。一个抽象产品只能生产一个产品。如果你对工厂方法还不太了解,那就往下看一个典型的工厂方法实现。工厂方法在JDK中有很多用途,比较典型的就是newArrayList<>().iterator();我们知道一个集合Large分支依赖于Collection接口,我们以ArrayList为例。Collection接口相当于产品的抽象父类,ArrayList相当于具体的产品。迭代器是一个抽象工厂。ArrayList中有一个Itr内部类,实现了Iterator接口。当我们调用集合的iterator()方法遍历对象时,会调用各个类的具体实现方法。4.抽象工厂有一天,产品A、B、C升级了。三款产品分别为红色和蓝色。如果仍然使用工厂方法,那将是一场灾难。具体的工厂实现类需要六个。这就引出了我们今天的第二种设计模式——抽象工厂。抽象工厂模式:提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的特定类。抽象工厂模式,又称Kit模式,属于对象创建模式。抽象工厂模式与工厂方法模式的区别在于,工厂方法模式针对的是产品层次结构。抽象工厂模式需要面对多个产品层次结构(各种颜色),一个工厂层次结构可以负责多个不同产品层次结构(不同颜色)中产品对象的创建。抽象工厂仍然有四种作用:AbstractFactory:抽象工厂ConcreteFactory:具体工厂AbstractProduct:抽象产品Product:具体产品我们开始改造工厂方法代码,既然要将产品归为一组,就应该将产品A,B分组,和C都是抽象出来的,然后让工厂类去实现每个产品的不同颜色,在概念上就是——用来创建一族相关或者相互依赖的对象。查看接口修改后的代码:Productabstractclass:/***@authortcy*@Date28-07-2022*/publicinterfaceProduct{publicvoidcommon();}ProductabstractAfamilyclass:/***@authortcy*@Date28-07-2022*/publicabstractclassProductAimplementsProduct{publicabstractvoidcommon();}产品抽象类B系列:/***@authortcy*@Date28-07-2022*/publicabstractclassProductBimplementsProduct{publicabstractvoidcommon();}具体的红色产品类A:/***@authortcy*@Date28-07-2022*/publicclassRedProductAextendsProductA{@Overridepublicvoidcommon(){System.out.println("这是红色产品A");}}特定的蓝色产品类A:/***@authortcy*@Date28-07-2022*/publicclassBlueProductAextendsProductA{@Overridepublicvoidcommon(){System.out.println("ThisisblueproductA”);}}抽象家庭工厂类:/***@authortcy*@Date28-07-2022*/publicinterfaceAbstractProductFactory{publicProductAcreateProduct(StringorderType);}实施家庭工厂类:/***@authortcy*@Date28-07-2022*/公共课程ConcreateProductAFactory实现AbstractProductFactory{@OverridepublicProductAcreateProduct(StringorderType){returnnewBlueProductA();}}Pharaoh类:/***@authortcy*@Date28-07-2022*/publicclassClient{ConcreateProductAFactorysimpleFactory;publicClient(ConcreateProductAFactorysimpleFactory){this.simpleFactory=simpleFactory;}publicProductAorderProduct(StringorderType){ProductA产品;product=simpleFactory.createProduct(orderType);//调用相应的方法product.common();System.out.println(product.getClass());退回产品;}publicstaticvoidmain(String[]args){Clientclient=newClient(newConcreateProductAFactory());client.orderProduct("A");这样日常的工厂类就可以实现A的所有产品系列类(红蓝)。抽象工厂模式隔离了具体类的生成,这样老王就不需要知道创建的是什么产品,并且与特定产品分离当产品系列中的多个对象设计为一起工作时,它保证客户将始终只使用来自同一产品系列的对象。增加新产品、新工厂很容易,但如果再增加一个关卡(颜色),修改代码就很痛苦了。抽象工厂模式在Spring中被广泛使用。通常,BeanFactory是一个管理bean的工厂,所有的工厂都是BeanFactory的子类。这样我们就可以通过IOC容器来管理对bean的访问,根据不同的策略调用getBean()方法来获取特定的对象。BeanFactory的子类主要有ClassPathXmlApplicationContext、XmlWebApplicationContext、StaticWebApplicationContext、StaticApplicationContext。在Spring中,DefaultListableBeanFactory实现了所有工厂的通用逻辑。