一、使用场景
- 在一些情况下,要创建的对象需要一系列复杂的初始化操作,比如查配置文件、查数据库表、初始化成员对象等,如果把这些函数放在构造函数中,会极大影响代码的可读性。不妨定义一个类来专门负责对象的创建,这样的类就是工厂类,这种做法就是工厂模式,在任何需要生成复杂对象的地方,都可以使用工厂模式。
- 在编码时不能预见需要创建那种类的实例。
- 系统不应依赖于产品类实例如何被创建、组合和表达的细节。
| 工厂模式 | SUM |
|---|---|
| 简单工厂 | 唯一工厂类,一个产品抽象类,工厂类的创建方法依据入参判断并创建具体产品对象 |
| 工厂方法 | 多个工厂类,一个产品抽象类,利用多态创建不同的产品对象,避免大量的if…else…判断 |
| 抽象工厂 | 多个工厂类,多个产品抽象类,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂主类的数量 |
二、作用
工厂模式一般配合策略模式使用一起使用。用来去优化大量的 if…else.. 或 switch…case…语句
三、 定义
简单工厂
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类。
- 抽象类或接口:定义了要创建的产品对象的接口;
- 具体实现:具有统一父类的具体类型的产品;
- 产品工厂:负责创建产品对象;
优点:工厂模式同样体现了开闭原则,将“创建具体的产品实现类”这部分变化的代码从不变化的代码“使用产品”中分离出来,之后想要新增产品时,只需要扩展工厂的实现即可。
缺点:简单工厂所对应的工厂类中会存在大量的if…else…,如果新增了实现类,又会多加一个else。这种做法扩展属性差,违背了开闭原则,也影响了可读性。
场景:在业务简单,工厂类不会经常更改的场景下使用 。
工厂模式
思考:为了解决上面简单工厂出现的大量if…else…问题,可以为每一个子类建立一个对应的工厂子类,这些子类实现同一个抽象工厂接口。
- 抽象工厂:声明了工厂方法的接口;
- 具体产品工厂:实现工厂方法的接口,负责创建产品对象;
- 产品抽象类或接口:定义工厂方法所创建的产品对象的接口;
- 具体产品实现:具有统一父类的具体类型的产品;
优点:这样创建不同业务时只需要实现不同的工厂子类。当有新的业务加入时,新建具体工厂继承抽象工厂,而不用修改任何一个类。
缺点:每一个业务对应一个工厂子类,在创建具体业务对象时,实例化不同的工厂子类。但是,如果业务涉及的子类越来越多,难道每一个子类都要对应一个工厂类吗?这样会使得系统类中类的个数成倍增加,增加了代码的复杂度。
抽象工厂
思考:为了缩减工厂实现子类的数量,不必给每一个产品分配一个工厂类,可以将产品进行分组,每组中的不同产品由同一个工厂类的不同方法来创建。
![]()
场景:
- 一个系统要独立于它的产品的创建、组合和表示时;
- 一个系统要由多个产品系列中的一个来配置时;
- 要强调一系列相关的产品对象的设计以便进行联合时使用;
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时;
优点:增加分组简单
缺点:分组中产品扩展困难
四、使用
简单工厂
文件类型解析枚举
public enum FileTypeResolveEnum {
FILE_TYPE_MP3("mp3","音频"),
FILE_TYPE_MP4("mp4","视频"),
FILE_TYPE_OTHER("other","其它");
//英文名称
private String eng;
//中文名称
private String chi;
FileTypeResolveEnum(String eng, String chi) {
this.eng = eng;
this.chi = chi;
}
public String getEng() {
return eng;
}
public void setEng(String eng) {
this.eng = eng;
}
public String getChi() {
return chi;
}
public void setChi(String chi) {
this.chi = chi;
}
}
定义创建某具体业务的接口
public interface IFileStrategy {
/**
* 获取文件类型
* @return
*/
public FileTypeResolveEnum gainFileType();
/**
* 该文件类型需要完成的业务方法
*/
public void doSomething();
}
mp3文件解析
@Service("mp3FileResolve")
public class Mp3FileResolve implements IFileStrategy{
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.FILE_TYPE_MP3;
}
@Override
public void doSomething() {
System.out.println("我干的是:"+FileTypeResolveEnum.FILE_TYPE_MP3.getChi()+" 的事!");
}
}
mp4文件解析
@Service("mp4FileResolve")
public class Mp4FileResolve implements IFileStrategy{
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.FILE_TYPE_MP4;
}
@Override
public void doSomething() {
System.out.println("我干的是:"+FileTypeResolveEnum.FILE_TYPE_MP4.getChi()+" 的事!");
}
}
other文件解析
@Service("otherFileResolve")
public class OtherFileResolve implements IFileStrategy{
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.FILE_TYPE_OTHER;
}
@Override
public void doSomething() {
System.out.println("我干的是:"+FileTypeResolveEnum.FILE_TYPE_OTHER.getChi()+" 的事!");
}
}
简单工厂类,全局访问点,只有该类能够进行调用,提供静态的方法调用
public class FactoryUse {
//按文件类型获取具体业务
public static IFileStrategy getFileStrategy(FileTypeResolveEnum resolveEnum){
IFileStrategy fileStrategy;
if(resolveEnum==FileTypeResolveEnum.FILE_TYPE_MP3){
fileStrategy=new Mp3FileResolve();
}else if(resolveEnum==FileTypeResolveEnum.FILE_TYPE_MP4){
fileStrategy=new Mp4FileResolve();
}else if(resolveEnum==FileTypeResolveEnum.FILE_TYPE_OTHER){
fileStrategy=new OtherFileResolve();
}else{
fileStrategy=new OtherFileResolve();
}
return fileStrategy;
}
}
结合策略模式使用简单工厂,更加的规范
@Component
public class FactoryUseByStrategy implements ApplicationContextAware {
private static Map<FileTypeResolveEnum,IFileStrategy> factoryMap = new ConcurrentHashMap<>();
public static IFileStrategy getFileStrategy(FileTypeResolveEnum resolveEnum){
return factoryMap.get(resolveEnum);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> beansOfType = applicationContext.getBeansOfType(IFileStrategy.class);
beansOfType.forEach((k,v)-> System.err.println(k+":"+v));
beansOfType.values().forEach(resolveService->factoryMap.put(resolveService.gainFileType(),resolveService));
}
}
测试
@Test
void contextLoads() {
FactoryUse.getFileStrategy(FileTypeResolveEnum.FILE_TYPE_MP3).doSomething();
FactoryUseByStrategy.getFileStrategy(FileTypeResolveEnum.FILE_TYPE_OTHER).doSomething();
}
工厂模式
国家产量类
public class CountryConstant {
//中国
public final static String CHINESE = "chinese";
//美国
public final static String AMERICAN = "american";
}
文件解析总工厂接口类(针对中国文件解析工厂和美国解析工厂)
public interface FileResolveFactory extends ApplicationContextAware {
//获取哪个(中国/美国)工厂的何种文件类型(mp3、mp4、other)解析
IFileStrategy gainFileResolve(FileTypeResolveEnum resolveEnum);
}
美国文件解析工厂
@Component
public class AmericanFileResolveFactory implements FileResolveFactory {
private static Map<FileTypeResolveEnum,IFileStrategy> americanFactoryMap = new ConcurrentHashMap<>();
@Override
public IFileStrategy gainFileResolve(FileTypeResolveEnum resolveEnum) {
return americanFactoryMap.get(resolveEnum);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> beansOfType = applicationContext.getBeansOfType(IFileStrategy.class);
beansOfType.forEach((k,v)->{
if (k.startsWith(CountryConstant.AMERICAN)) {
americanFactoryMap.put(v.gainFileType(),v);
}
});
}
}
中国文件解析工厂
@Component
public class ChineseFileResolveFactory implements FileResolveFactory {
private static Map<FileTypeResolveEnum,IFileStrategy> chineseFactoryMap = new ConcurrentHashMap<>();
@Override
public IFileStrategy gainFileResolve(FileTypeResolveEnum resolveEnum) {
return chineseFactoryMap.get(resolveEnum);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> beansOfType = applicationContext.getBeansOfType(IFileStrategy.class);
beansOfType.forEach((k,v)->{
if (k.startsWith(CountryConstant.CHINESE)) {
chineseFactoryMap.put(v.gainFileType(),v);
}
});
}
}
工厂方法统一访问
@Component
public class CountryFactoryUse implements ApplicationContextAware {
private static Map<String,FileResolveFactory> countryFactoryUseMap = new ConcurrentHashMap<>();
public static FileResolveFactory getCountryFileResolve(String country){
return countryFactoryUseMap.get(country);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
applicationContext.getBeansOfType(FileResolveFactory.class).forEach((k,v)->{
if (k.startsWith(CountryConstant.AMERICAN)) {
countryFactoryUseMap.put(CountryConstant.AMERICAN,v);
}
if (k.startsWith(CountryConstant.CHINESE)) {
countryFactoryUseMap.put(CountryConstant.CHINESE,v);
}
});
}
}
测试
@Test
void contextLoads() {
CountryFactoryUse
.getCountryFileResolve(CountryConstant.AMERICAN)
.gainFileResolve(FileTypeResolveEnum.FILE_TYPE_MP3)
.doSomething();
CountryFactoryUse
.getCountryFileResolve(CountryConstant.CHINESE)
.gainFileResolve(FileTypeResolveEnum.FILE_TYPE_MP3)
.doSomething();
}
//打印结果
抽象工厂
生产手机接口
public interface Phone {
void doPhone();
}
生产电脑接口
public interface Computer {
void doComputer();
}
生产某品牌电脑和手机的抽象类
/**
* 生产某品牌电脑和手机的抽象类
* 好处:要生产某品牌的电脑直接实现该接口就可以
* 坏处:假如该品牌要生产其它产品就需要更改此接口,违反了开闭原则
*/
public interface BrandFactory {
//生产电脑
Computer produceComputer(String computer);
//生产手机
Phone producePhone(String phone);
}
生产小米电脑、手机
/**
* 生产小米电脑
*/
@Service
public class XiaoMIComputer implements Computer {
@Override
public void doComputer() {
System.out.println("produce xiaoMiComputer");
}
}
/**
* 生产小米电脑:小米pro 14
*/
@Service
public class XiaoMiComputerPro14 extends XiaoMIComputer {
@Override
public void doComputer() {
System.out.println("produce xiaoMiComputerPro14");
}
}
/**
* 生产小米电脑:红米BookPro
*/
@Service
public class XiaoMiComputerRedmiBookPro extends XiaoMIComputer {
@Override
public void doComputer() {
System.out.println("produce xiaoMiComputerRedmiBookPro");
}
}
/**
* 生产小米手机
*/
@Service
public class XiaoMIPhone implements Phone {
@Override
public void doPhone() {
System.out.println("produce xiaoMiPhone");
}
}
/**
* 生产小米11Utra
*/
@Service
public class XiaoMiPhone11Utra extends XiaoMIPhone {
@Override
public void doPhone() {
System.out.println("produce xiaoMiPhone11Utra");
}
}
/**
* 生产小米12Pros
*/
@Service
public class XiaoMiPhone12Pros extends XiaoMIPhone {
@Override
public void doPhone() {
System.out.println("produce xiaoMiPhone12Pros");
}
}
生产苹果电脑、手机
/**
* 生产苹果电脑
*/
@Service
public class PinGuoComputer implements Computer {
@Override
public void doComputer() {
System.out.println("produce pinGuoComputer");
}
}
/**
* 生产苹果笔记本电脑:苹果MacBookPro
*/
@Service
public class PinGuoComputerMacBookPro extends PinGuoComputer {
@Override
public void doComputer() {
System.out.println("produce pinGuoComputerMacBookPro");
}
}
/**
* 生产苹果笔记本电脑:苹果MacMini
*/
@Service
public class PinGuoComputerMacMini extends PinGuoComputer {
@Override
public void doComputer() {
System.out.println("produce pinGuoComputerMacMini");
}
}
/**
* 生产苹果手机
*/
@Service
public class PinGuoPhone implements Phone {
@Override
public void doPhone() {
System.out.println("produce pinGuoPhone");
}
}
/**
* 生产苹果11ProMax
*/
@Service
public class PinGuoPhone11ProMax extends PinGuoPhone {
@Override
public void doPhone() {
System.out.println("produce pinGuoPhone11ProMax");
}
}
/**
* 生产苹果12ProMax
*/
@Service
public class PinGuoPhone12ProMax extends PinGuoPhone {
@Override
public void doPhone() {
System.out.println("produce pinGuoPhone12ProMax");
}
}
相关常量
/**
* 产品常量
*/
public class BrandConstant {
/**
* 品牌
*/
public static class Brand{
public final static String XIAOMI= "xiaoMi";
public final static String PINGUO= "pinGuo";
}
/**
* 电脑常量
*/
public static class Computer{
/**
* 小米
*/
public static class XiaoMi{
public final static String COMPUTER_PRO14= "xiaoMiComputerPro14";
public final static String COMPUTER_REDMIBOOKPRO= "xiaoMiComputerRedmiBookPro";
}
/**
* 苹果
*/
public static class PinGuo{
public final static String COMPUTER_MACBOOKPRO= "PinGuoComputerMacBookPro";
public final static String COMPUTER_MACMINI= "PinGuoComputerMacMini";
}
}
/**
* 手机常量
*/
public static class phone{
/**
* 小米
*/
public static class XiaoMi{
public final static String PHONE_11UTRA= "xiaoMiPhone11Utra";
public final static String PHONE_12PROS= "xiaoMiPhone12Pros";
}
/**
* 苹果
*/
public static class PinGuo{
public final static String PHONE_11PROMAX= "PinGuoPhone11ProMax";
public final static String PHONE_12PROMAX= "PinGuoPhone12ProMax";
}
}
}
生产电脑、手机的厂商(苹果工厂、小米工厂)
/**
* 苹果工厂
*/
@Service
public class PinGuoFactory implements BrandFactory {
@Override
public Computer produceComputer(String computer) {
if (Objects.equals(computer, BrandConstant.Computer.PinGuo.COMPUTER_MACBOOKPRO)) {
return new PinGuoComputerMacBookPro();
}
if (Objects.equals(computer, BrandConstant.Computer.PinGuo.COMPUTER_MACMINI)) {
return new PinGuoComputerMacMini();
}
return new PinGuoComputer();
}
@Override
public Phone producePhone(String phone) {
if (Objects.equals(phone, BrandConstant.phone.PinGuo.PHONE_11PROMAX)) {
return new PinGuoPhone11ProMax();
}
if (Objects.equals(phone, BrandConstant.phone.PinGuo.PHONE_12PROMAX)) {
return new PinGuoPhone12ProMax();
}
return new PinGuoPhone();
}
}
/**
* 小米工厂
* 这儿又可以根据生产什么类型的电脑和什么类型的手机,传入相关参数
*/
@Service
public class XiaoMiFactory implements BrandFactory {
@Override
public Computer produceComputer(String computer) {
if (Objects.equals(computer, BrandConstant.Computer.XiaoMi.COMPUTER_PRO14)) {
return new XiaoMiComputerPro14();
}
if (Objects.equals(computer, BrandConstant.Computer.XiaoMi.COMPUTER_REDMIBOOKPRO)) {
return new XiaoMiComputerRedmiBookPro();
}
return new XiaoMIComputer();
}
@Override
public Phone producePhone(String phone) {
if (Objects.equals(phone, BrandConstant.phone.XiaoMi.PHONE_11UTRA)) {
return new XiaoMiPhone11Utra();
}
if (Objects.equals(phone, BrandConstant.phone.XiaoMi.PHONE_12PROS)) {
return new XiaoMiPhone12Pros();
}
return new XiaoMIPhone();
}
}
抽象工厂统一访问使用
@Component
public class BrandFactoryUse implements ApplicationContextAware {
private static Map<String, BrandFactory> brandFactoryMap = new ConcurrentHashMap<>();
public static BrandFactory getBrandFactory(String brandFactory){
return brandFactoryMap.get(brandFactory);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, BrandFactory> beansOfType = applicationContext.getBeansOfType(BrandFactory.class);
beansOfType.forEach((k,v)->{
if (k.startsWith(BrandConstant.Brand.XIAOMI)) {
brandFactoryMap.put(BrandConstant.Brand.XIAOMI,v);
}
if (k.startsWith(BrandConstant.Brand.PINGUO)) {
brandFactoryMap.put(BrandConstant.Brand.PINGUO,v);
}
});
}
}
测试抽象工厂
@Test
void contextLoads() {
//生产苹果电脑:COMPUTER_MACBOOKPRO
BrandFactoryUse
.getBrandFactory(BrandConstant.Brand.PINGUO)
.produceComputer(BrandConstant.Computer.PinGuo.COMPUTER_MACBOOKPRO)
.doComputer();
//生产苹果手机:PHONE_11PROMAX
BrandFactoryUse
.getBrandFactory(BrandConstant.Brand.PINGUO)
.producePhone(BrandConstant.phone.PinGuo.PHONE_11PROMAX)
.doPhone();
//生产小米电脑:COMPUTER_PRO14
BrandFactoryUse
.getBrandFactory(BrandConstant.Brand.XIAOMI)
.produceComputer(BrandConstant.Computer.XiaoMi.COMPUTER_PRO14)
.doComputer();
//生产小米手机:PHONE_11UTRA
BrandFactoryUse
.getBrandFactory(BrandConstant.Brand.XIAOMI)
.producePhone(BrandConstant.phone.XiaoMi.PHONE_11UTRA)
.doPhone();
}
//打印结果...
produce pinGuoComputerMacBookPro
produce pinGuoPhone11ProMax
produce xiaoMiComputerPro14
produce xiaoMiPhone11Utra
五、参考
参考文章——抽象工厂