工厂模式


一、使用场景

  1. 在一些情况下,要创建的对象需要一系列复杂的初始化操作,比如查配置文件、查数据库表、初始化成员对象等,如果把这些函数放在构造函数中,会极大影响代码的可读性。不妨定义一个类来专门负责对象的创建,这样的类就是工厂类,这种做法就是工厂模式,在任何需要生成复杂对象的地方,都可以使用工厂模式。
  2. 在编码时不能预见需要创建那种类的实例。
  3. 系统不应依赖于产品类实例如何被创建、组合和表达的细节。
工厂模式 SUM
简单工厂 唯一工厂类,一个产品抽象类,工厂类的创建方法依据入参判断并创建具体产品对象
工厂方法 多个工厂类,一个产品抽象类,利用多态创建不同的产品对象,避免大量的if…else…判断
抽象工厂 多个工厂类,多个产品抽象类,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂主类的数量

二、作用

工厂模式一般配合策略模式使用一起使用。用来去优化大量的 if…else.. 或 switch…case…语句

三、 定义

简单工厂

定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类。

  • 抽象类或接口:定义了要创建的产品对象的接口;
  • 具体实现:具有统一父类的具体类型的产品;
  • 产品工厂:负责创建产品对象;

优点:工厂模式同样体现了开闭原则,将“创建具体的产品实现类”这部分变化的代码从不变化的代码“使用产品”中分离出来,之后想要新增产品时,只需要扩展工厂的实现即可。

缺点:简单工厂所对应的工厂类中会存在大量的if…else…,如果新增了实现类,又会多加一个else。这种做法扩展属性差,违背了开闭原则,也影响了可读性。

场景:在业务简单,工厂类不会经常更改的场景下使用 。

img

工厂模式

思考:为了解决上面简单工厂出现的大量if…else…问题,可以为每一个子类建立一个对应的工厂子类,这些子类实现同一个抽象工厂接口。

  • 抽象工厂:声明了工厂方法的接口;
  • 具体产品工厂:实现工厂方法的接口,负责创建产品对象;
  • 产品抽象类或接口:定义工厂方法所创建的产品对象的接口;
  • 具体产品实现:具有统一父类的具体类型的产品;

优点:这样创建不同业务时只需要实现不同的工厂子类。当有新的业务加入时,新建具体工厂继承抽象工厂,而不用修改任何一个类。

缺点:每一个业务对应一个工厂子类,在创建具体业务对象时,实例化不同的工厂子类。但是,如果业务涉及的子类越来越多,难道每一个子类都要对应一个工厂类吗?这样会使得系统类中类的个数成倍增加,增加了代码的复杂度。

img

抽象工厂

思考:为了缩减工厂实现子类的数量,不必给每一个产品分配一个工厂类,可以将产品进行分组,每组中的不同产品由同一个工厂类的不同方法来创建。

image-20220428173103600

场景:

  1. 一个系统要独立于它的产品的创建、组合和表示时;
  2. 一个系统要由多个产品系列中的一个来配置时;
  3. 要强调一系列相关的产品对象的设计以便进行联合时使用;
  4. 当你提供一个产品类库,而只想显示它们的接口而不是实现时;

优点:增加分组简单

缺点:分组中产品扩展困难

img

四、使用

image-20220429165502101

简单工厂

文件类型解析枚举

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();
    }
//打印结果

抽象工厂

image-20220430000423127

生产手机接口

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

五、参考

参考文章

参考文章——抽象工厂


文章作者: LJH
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LJH !
  目录