一文搞懂工厂模式(简单工厂/抽象工厂/工厂方法)

x33g5p2x  于2021-12-07 转载在 其他  
字(9.5k)|赞(0)|评价(0)|浏览(396)

创建型模式

  • 创建型模式关注对象的创建过程,它将对象的创建和使用分离,在使用对象时无须知道对象的创建细节。这样可以降低系统的耦合度。使得相同的创建过程可以多次复用,且修改二者中的一个对另一个几乎不造成任何影响或很少的影响。

创建者模式概述

为了让大家更生动的理解,下面我们来举个典型的栗子:

  • 如果想吃苹果,至少有两种获取苹果的方式,第一种方式是自己种苹果树,等待苹果树开花结果,在经过若干天漫长的等待后再慢慢品尝自己的劳动成果,第二种方式是由专门的苹果种植户或农场将苹果种好,放在超市或水果摊的架子上自己选购,一手掏钱一手提货,只要有钱马上就可以吃到苹果。
  • 对于这两种方式,一般情况下人们会选择哪种呢?毫无疑问,选择第二种的人会更多,毕竞种苹果的是少数,吃苹果的是多数。为什么呢?其一,因为苹果由专门的种植户和农场来生产,有一套规范的生产流程,其培育过程更加专业﹔其二,对于用户来说,只需要通过简单的方式即可获得苹果,无须关心其种植过程,极大提高用户获取苹果的效率;其三,由于将苹果的生产和苹果的消费分离,相同的生产者可以将苹果卖给不同的消费者,同一个消费者也可以货比三家,从不同的生产者那里购买苹果,增强了灵活性。既然有这么多优点,还有必要每个人自己种苹果自己吃吗?这个答案不言而喻。

  • 在面向对象软件开发过程中也经常存在类似自己种苹果还是直接去买苹果的情况,如需要某个类的一个实例化对象,是在代码中直接使用new关键字来进行实例化,还是通过已有的实例工厂间接获取对象实例?而创建型模式正是为解决这类问题而诞生的,不同的创建型模式从不同角度解决了苹果从何而来的问题。

创建者模式简介

  • 创建型模式主要包括如下6种模式。
模式名称定义概述
简单工厂模式根据传人的参数即可返回所需的对象,而不需要知道具体类的类名根据提供给它的数据,返回几个可能类中的一个类的实例。通常它返回的类都有一个公共的父类和公共的方法。简单工厂模式不属于GoF设计模式
工厂方法模式定义一个用于创建对象的接口-让子类决定将哪一个类实例化。工厂方法模式使一个类的实例化延迟到其子类将基一类对象的创建过程封装在单独的类中,通过引入抽象层的方式来使得对象的创建和使用更为灵活
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类在一个类中可以创建多个不同类型的对象,这些对象所对应的类型都源于抽象层,使得系统具有极佳的扩展性和灵活性
建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示一步一步构造一个由多个部分组成的复杂对象
原型模式用原型实例指定创建对象的种类,并且通过复制这个原型来创建新的对象通过复制已有对象创建出相似的其他对象
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点控制系统中所创建的对象实例的个数

下面将带大家来学习这些巧妙的模式,并将他们运用到实际开发中,让大家更加轻松的吃到 “苹果

开始学习之前先带大家了解创建型模式的概念

简单工厂模式

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

简介

  • 简单工厂模式又称为静态工厂方法模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
  • 如下图所示,用户无须知道苹果,橙﹑香蕉如何创建,只需要知道水果的名字则可得到对应的水果。

  • 一个软件系统可以提供多个外观不同的按钮(如圆形按钮、矩形按钮﹑菱形按钮等),这些按钮都源自同一个基类,不过在继承基类后不同的子类修改了部分属性从而使得它们可以呈现不同的外观,如果我们希望在使用这些按钮时,不需要知道这些具体按钮类的名字,只需要知道表示该按钮类的一个参数,并提供一个调用方便的方法,把该参数传入方法即可返回一个相应的按钮对象,此时,就可以使用简单工厂模式。

模式结构

简单工厂模式结构比较简单,其核心是工厂类。包含如下角色。

  1. Factory(工厂角色)

工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。在工厂类中提供了静态的工厂方法 factoryMethod() ,它返回一个抽象产品类Product,所有的具体产品都是抽象产品的子类。

  1. Product(抽象产品角色)

抽象产品角色是简单工厂模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个工厂方法,因为所有创建的具体产品对象都是其子类对象。

  1. ConcreteProduct(具体产品角色)

具体产品角色是简单工厂模式的创建目标,所有创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色。需要实现定义在抽象产品中的抽象方法。

某销售管理系统支持多种支付方式如现金支付(CashPay)、信用卡支付(CreditcardPay),代金券支付(VoucherPay)等,在设计中如果不使用简单工厂模式,可能会存在如下支付方法。

public void pay(String type){
	if(type.equalsIgnoreCase("cash"){
		//现金支付处理代码
	}
	else if(type.equalsIgnoreCase("creditcard")){
		//信用卡支付处理代码
	}
	else if(type.equalsIgnoreCase("voucher")){
		//信用卡支付处理代码
	}
	else{
		...
	}
}
  • 问题:由于不同的支付方式其支付处理方法不一致,因此该方法源代码将相当冗长,而且每当需要增加新的支付方式时,不得不修改这段if…else…代码,增加很多新的支付处理代码。代码越长意味着维护工作量越大,测试难度也越大,扩展和修改也越不灵活。因此可以考虑使用简单工厂模式对其进行重构。

为了保证系统的扩展性并将各种支付类型对象的创建封装在一个统一的方法中,需要引人抽象支付方式类,它定义了抽象的支付方法,抽象支付方法类定义如下

public abstract class AbstractPay{
	public abstract void pay();
}

将每一种支付方式封装在一个独立的类中,各个支付方式类相对独立,修改其一对其他类无任何影响,这些独立的支付方式类充当具体产品类的角色,是抽象支付方式类的子类,如现金支付类信用卡支付类定义如下

public class CashPay extends AbstractPay{
	public void pay(){
		//现金支付处理代码
	}
}
public class CreditcardPay extends AbstractPay{
	public void pay(){
		//信用卡支付处理代码
	}
}

提供一个代码相对简单,而且只负责创建对象而不必关心对象细节的工厂类来创建各种具体的支付方式产品类,注意其工厂方法的返回类型是抽象类型,支付方式工厂类定义如下

public class PayMethodFactory{
	public static AbstractPay getPayMethod(String type){
		if(type.equalsIgnoreCase("cash")){
			return new CashPay();		//根据参数创建具体产品
		}else if(type.equalsIgnoreCase("creditcard")){
			return new CreditcardPay();		//根据参数创建具体产品
		}
	}
}

通过对原有设计的重构可以发现,在使用了简单工厂模式之后,系统中类的个数增加,每一种支付处理方式都封装到单独的类中,而且工厂类中只有简单的判断逻辑代码,不需要关心具体的业务处理过程,很好地满足了单一职责原则。在增加新的支付方式时,只需要添加一个新的具体支付类并实现其中的 pay()方法,同时对工厂类PayMethodFactory做简单的修改即可,无需对原有的代码进行大面积的改动。

  • 简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数就可以获取你所需的对象,而无需知道其创建细节。

练习案例

光说不练是没有用的,下面通过两个案例带你进一步的学习理解该模式。

案例1

某电视机厂专为各知名电视机品牌代工生产各类电视机,当需要海尔牌电视机时只需要在调用该工厂的工厂方法时传入参数 Haier,需要小米电视机时只需要传入参数XiaoMi,工厂可以根据传入的不同参数返回不同品牌的电视机。现使用简单工厂模式来模拟该电视机工厂的生产过程。

  1. 抽象产品类TV(电视机类)

TV作为抽象产品类,它可以是一个接口,也可以是一个抽象类,其中包含了所有产品都具有的业务方法 play()。

  1. 具体产品类HaierTV(海尔电视机类)

HaierTV是抽象产品TV接口的子类,它是一种具体产品,实现了在TV接口中定义的业务方法 play()。

  1. 具体产品类XiaoMiTV(小米电视机类)

XiaoMiTV是抽象产品TV接口的另一个子类,即另一种具体产品,不同的具体产品在实现业务方法时有所不同。

  1. 工厂类TVFactory(电视机工厂类)

TVFactory是工厂类,它是整个系统的核心,它提供了静态工厂方法 produceTV(),工厂方法中包含一个字符串类型的参数,在内部业务逻辑中根据参数值的不同实例化不同的具体产品类,返回相应的对象。

  1. 创建客户端并测试

传入参数XiaoMi

如果希望该系统能够支持TCL牌电视机﹐则需要增加一个新的具体产品类TCLTV,同时还需要修改工厂类TVFactory中的工厂方法,在其判断逻辑中增加一个新的分支即可。

案例2

在某ОA系统中,系统根据对比用户在登录时输入的账号和密码以及在数据库中存储的账号和密码是否一致来进行身份验证,如果验证通过,则取出存储在数据库中的用户权限等级(以整数形式存储),根据不同的权限等级创建不同等级的用户对象,不同等级的用户对象拥有不同的操作权限。现使用简单工厂模式来设计该权限管理模块。

  1. 抽象产品类 User(用户类)

抽象类User作为抽象产品,它是各种具体用户类的父类,其中提供了一系列所有用户子类公有的方法,如“修改个人资料”等,同时它也定义了抽象方法,以便不同的子类分别来实现。

  1. 具体产品类 Employee(员工类)

Employee类是User类的子类,它继承了公有的方法 sameOperation(),同时也覆盖了抽象方法diffOperation()。

  1. 具体产品类 Manager(经理类)

Manager类也是User类的子类,是具体产品类的一种。

  1. 具体产品类Administrator(管理员类)

Administrator类也是User类的子类,是具体产品类的一种。

  1. 工厂类 UserFactory(用户工厂类)

UserFactory类是核心工厂类,通过改变工厂方法 getUser()中的参数可以创建不同类型的用户。

  1. 辅助类 UserDao(用户表数据访问类)

在实例中,我们模拟数据库访问操作,提供了一个数据访问类UserDAO,其中定义了一个方法 findPermission(),用于根据用户名和密码查询权限,在真实项目的开发中,只需要把方法中的模拟代码改成数据库操作代码即可,返回的权限值将作为工厂类UserFactory的参数值。

  1. 创建客户端Client并测试

在客户端测试类中,我们模拟用户zhangsan 的登录过程,在实际开发中,账号和密码来自表示层,如文本框和密码框的输入值或网页表单输人。在代码中产品对象使用抽象层类User来进行定义,通过调用UserDAO中的findPermission()方法来根据账号和密码查询权限值,然后以该权限值为参数调用工厂类UserFactory的静态方法 getUser()获取具体产品对象。

如果出现新类型的用户,需要添加一个User类的子类并修改工厂类UserFactory中工厂方法的判断逻辑,但是如果只是修改现有用户的权限,只需修改数据库中对应字段值即可,客户端代码无须做任何修改。

简单工厂模式总结

  • 优点: 使用简单工厂模式客户端无需知道具体创建的类名,传对应的参数即可,工厂决定再什么时候创建什么对象,还可以引入一些配置文件,再不修改客户端代码的情况下更换增加具体的产品类。再一定程度上提高了系统的灵活性。
  • 缺点: 由于工厂类集中了所有产品的创建逻辑,一旦添加新产品就不得不改工厂逻辑,再产品较多时,会增加系统中类的数量,也会造成工厂逻辑过于复杂,不易理解,扩展和维护,一旦工厂不能正常工作,整个系统都会收到牵连。

该模式适用于创建对象较少,不会造成工厂业务逻辑复杂,客户端只需传惨不关注创建细节的情况下。

工厂方法模式

工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的缺陷,更好地符合“开闭原则”的要求。再上文中我们可以清楚的看到简单工厂模式最大的缺陷,所有产品都是由一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。

简介

  • 工厂方法模式(Factory Method Pattern)定义: 工厂方法模式又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

模式结构

工厂方法模式包含如下角色。

  1. Product(抽象产品)

抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的共同父类或接口。

  1. ConcreteProduct(具体产品)

具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间一对应。

  1. Factory(抽象工厂)

在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,它与应用程序无关。任何在模式中创建对象的工厂类都必须实现该接口。

  1. ConcreteFactory(具体工厂)

具体工厂是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。在具体工厂类中包含与应用程序密切相关的逻辑,并且接受应用程序调用以创建产品对象。

练习案例

下面我们通过一个案例对上述简单工厂的代码进行改造,理解工厂方法模式。

案例1

在上面学习简单工厂模式时我们通过一个电视机代工生产工厂来生产电视机,当需要增加新的品牌的电视机时不得不修改工厂类中的工厂方法,违反了“开闭原则”。

为了让增加新品牌电视机更加方便,可以通过工厂方法模式对该电视机厂进行进一步重构。可以将原有的工厂进行分割,为每种品牌的电视机提供一个子工厂,海尔工厂专门负责生产海尔电视机,小米工厂专门负责生产小米电视机,如果需要生产TCL电视机或创维电视机,只需要对应增加一个新的TCL工厂或创维工厂即可,原有的工厂无须做任何修改,使得整个系统具有更好的灵活性和可扩展性。

  1. 抽象产品类TV(电视机类)

TV作为抽象产品类,它可以是一个接口,也可以是一个抽象类,其中包含了所有产品都具有的业务方法 play()。

  1. 具体产品类HaierTV(海尔电视机类)

HaierTV是抽象产品TV接口的子类,它是一种具体产品,实现了在TV接口中定义的业务方法 play()。

  1. 具体产品类XiaoMiTV(小米电视机类)

XiaoMiTV是抽象产品TV接口的另一个子类

  1. 工厂类TVFactory(电视机工厂类)

TVFactory是抽象工厂类,它可以是一个接口,也可以是一个抽象类,它包含了抽象的工厂方法produceTV),返回个抽象产品TV类型的对象。

  1. 具体工厂类HaierTVFactory(海尔电视机工厂类)

HaierTVFactory是具体工厂类,它是抽象工厂类 TVFactory 的子类,实现了抽象工厂方法 produceTV(),在工厂方法中创建并返回一个对象的具体产品。

  1. 具体工厂类XiaoMiTVFactory(小米电视机工厂类)

XiaoMiTVFactory它是抽象工厂类 TVFactory 的另一个子类,实现了抽象工厂方法 produceTV(),在工厂方法中创建并返回一个对象的具体产品

  1. 客户端测试类Client

如果需要增加一种新的类型的电视机,如TCL电视机,首先需要增加一个新的具体产品类TCLTV,再对应增加一个具体工厂类TCLTVFactory即可。

工厂方法模式总结

  • 优缺点: 工厂方法模式的主要优点是增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;其缺点在于增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。

该模式适用情况:一个类不知道它所需要的对象的类,另一个类通过其子类来指定创建哪个对象。将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。

抽象工厂模式

抽象工厂模式也是常见的创建型设计模式之一,它比工厂方法模式的抽象程度更高。在工厂方法模式中具体工厂只需要生产一种具体产品,但是在抽象工厂模式中。具体工厂可以生产相关的一组具体产品,这样的一组产品称之为产品族,产品族中的每一个产品都分属于某一个产品继承等级结构。

简介

抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。

  • 抽象工厂模式(Abstract Factory Pattern)定义: 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

模式结构

与简单工厂模式和工厂方法模式相比较,抽象工厂模式结构较为复杂,包含如下角色。

  1. AbstractFactory(抽象工厂)

抽象工厂用于声明生成抽象产品的方法,在一个抽象工厂中可以定义一组方法,每一个方法对应一个产品等级结构。

  1. ConcreteFactory(具体工厂)

具体工厂实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。

  1. AbstractProduct(抽象产品)

抽象产品为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法。

  1. ConcreteProduct(具体产品)

具体产品定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。

练习案例

下面依旧通过实例来进一步学习并理解抽象工厂模式。

案例1

一个电器工厂可以产生多种类型的电器,如海尔工厂可以生产海尔电视机、海尔空调等,TCL工厂可以生产TCL电视机、TCL空调等,相同品牌的电器构成一个产品族,而相同类型的电器构成了一个产品等级结构,现使用抽象工厂模式模拟该场景。

  1. 抽象产品类 Television(电视机类)

Television是一种抽象产品类,它可以是一个接口,也可以是一个抽象类,其中包含业务方法 play(的声明。

  1. 具体产品类 HaierTelevision(海尔电视机)

HaierTelevision是 Television的子类,实现了在Television中定义的业务方法 play() 。

  1. 具体产品类TCLTelevision(TCL 电视机类)

TCLTelevision是 Television的另一个子类,实现了在Television中定义的业务方法play()。Television,HaierTelevision和 TCLTelevision构成了一个产品等级结构。

  1. 抽象产品类 AirConditioner(空调类)

AirConditioner是另一种抽象产品类,它可以是一个接口,也可以是一个抽象类,其中包含业务方法changeTemperature()的声明。

  1. 具体产品类 HairAirConditioner(海尔空调类)

HairAirConditioner是 AirConditioner的子类,实现了在AirConditioner中定义的业务方法 changeTemperature()。

  1. 具体产品类 TCLAirConditioner(TCL空调类)

TCLAirConditioner是 AirConditioner的另一个子类,实现了在AirConditioner中定义的业务方法changeTemperaturc ()。AirConditioncr, HairAirConditioner和TCLAirConditioner构成了一个产品等级结构。

  1. 抽象工厂类EFactory(电器工厂类)

EFactory类是抽象工厂类,其中定义了抽象工厂方法,针对每一个产品族的产品都提供了一个对应的工厂方法。

  1. 具体工厂类 HaierFactory(海尔工厂类)

HaierFactory是 EFactory的一个子类,实现了在EFactory中定义的工厂方法,用于创建具体产品对象。HaierFactory所生产的具体产品构成了一个产品族。

  1. 具体工厂类TCLFactory(TCL工厂类)

TCLFactory是 EFactory的另一个子类,实现了在EFactory中定义的工厂方法,用于创建具体产品对象。TCLFactory所生产的具体产品构成了一个产品族。

EFactory 、 HaierFactory和 TCLFactory构成了一个工厂等级结构。

  1. 客户端测试类Colient

如果要增加一种新品牌的电器,即增加一个新的产品族,如增加海信电视机和海信空调,则只需要对应增加一个具体工厂即可,再将配置文件中具体工厂类类名改为新增的工厂类类名,原有代码无须做任何修改。

但是如果要增加一种新的产品,如增加一种新的电器产品洗衣机,则原有类库代码需要做较大的修改,在抽象工厂中需要声明一个生产洗衣机的方法,所有的具体工厂类都需要实现该方法,将导致系统不再符合“开闭原则”。

抽象工厂模式总结

  • 优缺点: 抽象工厂模式的主要优点是隔离了具体类的生成,使得客户并不需要知道什么被创建,而且每次可以通过具体工厂类创建一个产品族中的多个对象,增加或者替换产品族比较方便,增加新的具体工厂和产品族很方便;主要缺点在于增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。

抽象工厂模式适用情况:一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,系统中有多于一个的产品族,而每次只使用其中某一产品族,属于同一个产品族的产品将在一起使用,系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

到此三个简单工厂模式就整理完成了,建议大家跟着案例走一遍想获取代码的可以再主页资源中直接下载。

最后感谢大家支持!😊更多精彩文章还请访问:https://blog.csdn.net/weixin_45692705?spm=1011.2124.3001.5343

相关文章