简略工厂形式

简略工厂形式是指由一个工厂目标决议常见出哪一种产品类的实例,它不属于GOF23中规划形式,简略工厂形式适用于工厂类负责创立类较少的状况,以及需求创立的类不常改动同时也不长增删负责创立的类。调用者只需求传递参数即可,不需求联系创立的逻辑,工厂出产对应的实例给客户端。

下面通过付出场景实践阐明一下简略工厂办法

付出示例(无规划形式状况)

public interface IPay {
    String getName();
    Double queryBalance(String uid);
    default void pay(String uid,Double price){
        Double currentAmount  = queryBalance(uid);
        if(currentAmount < price){
            System.out.println(getName() + "余额缺乏");
        }else{
            System.out.println(getName() + "付出成功");
        }
    }
}
public class AliPay implements IPay{
    @Override
    public String getName() {
        return "付出宝";
    }
    @Override
    public Double queryBalance(String uid) {
        return 900.0;
    }
}
public class UnionPay implements IPay{
    @Override
    public String getName() {
        return "银行卡";
    }
    @Override
    public Double queryBalance(String uid) {
        return 10000.0;
    }
}
public class WeChatPay implements IPay{
    @Override
    public String getName() {
        return "微信付出";
    }
    @Override
    public Double queryBalance(String uid) {
        return 200.0;
    }
}

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)
从类图中就能够看出客户端是需求依赖完结类的,咱们当时举的比如实例化仍是很简略的,假如实例化需求更复杂的代码客户端的代码也会看起来很臃肿。

付出示例(简略工厂形式)

创立一个工厂类

public class PayFactory {
    public static IPay create(String patMethod){
        return switch (patMethod) {
            case "WeChatPay" -> new WeChatPay();
            case "UnionPay" -> new UnionPay();
            default -> new AliPay();
        };
    }
}

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)
能够发现客户端已经和目标的创立解耦了,客户端只需求关怀自己要运用那种办法。当然上述的写法仍是能够优化的,由于后续持续增加付出办法还要修正create办法还要增加一个case,所以咱们能够持续优化一下代码

付出示例(简略工厂形式+反射)

修正一下工厂创立办法:

public class PayFactory {
    public static IPay create(String patMethod){
        if(!(patMethod == null || "".equals(patMethod))){
            try {
                return (IPay) Class.forName(patMethod).getDeclaredConstructor().newInstance();
            }catch (NoSuchMethodException | ClassNotFoundException | InstantiationException | IllegalAccessException |
                    InvocationTargetException e){
                e.printStackTrace();
            }
        }
        return null;
    }
}
public static void main(String[] args) {
    IPay aliPay = PayFactory.create("com.example.demo.test.pay.AliPay");
    aliPay.pay("123123",100.0);
}

这样修正的长处是后续假如想增加付出办法不需求再修正create办法,可是还有个坏处是字符串可控性不好咱们能够针对这种状况持续优化一下

付出示例(简略工厂形式+反射优化)

public class PayFactory {
    public static IPay create(Class<? extends IPay> clz){
        try {
            clz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return null;
    }
}
public static void main(String[] args) {
    IPay aliPay = PayFactory.create(AliPay.class);
    assert aliPay != null;
    aliPay.pay("123123",100.0);
}

这样写便是稍微完美点的简略工厂形式的完结了

简略工厂办法在源码中的运用

Calendar类的创立

public static Calendar getInstance(TimeZone zone)
{
    return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
private static Calendar createCalendar(TimeZone zone,
                                       Locale aLocale)
{
    CalendarProvider provider =
        LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                             .getCalendarProvider();
    if (provider != null) {
        try {
            return provider.getInstance(zone, aLocale);
        } catch (IllegalArgumentException iae) {
            // fall back to the default instantiation
        }
    }
    Calendar cal = null;
    if (aLocale.hasExtensions()) {
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype != null) {
            cal = switch (caltype) {
                case "buddhist" -> new BuddhistCalendar(zone, aLocale);
                case "japanese" -> new JapaneseImperialCalendar(zone, aLocale);
                case "gregory"  -> new GregorianCalendar(zone, aLocale);
                default         -> null;
            };
        }
    }
    if (cal == null) {
        // If no known calendar type is explicitly specified,
        // perform the traditional way to create a Calendar:
        // create a BuddhistCalendar for th_TH locale,
        // a JapaneseImperialCalendar for ja_JP_JP locale, or
        // a GregorianCalendar for any other locales.
        // NOTE: The language, country and variant strings are interned.
        if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone, aLocale);
        } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                   && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone, aLocale);
        } else {
            cal = new GregorianCalendar(zone, aLocale);
        }
    }
    return cal;
}

Calendar为什么运用简略工厂形式其实也是很好了解的,由于时区、国家这些事基本上很难改变的,所以这个工厂创立完结之后工厂办法是基本上不需求改动的,所以就简略点运用简略工厂形式比较合适,代码也好了解。

所以在咱们往常开发的过程中也不是说必须的要契合开闭准则,类似的状况也能够运用简略工厂形式,找到最合适你们代码逻辑的形式,不是强加硬套。

适用场景

  1. 创立需求许多重复代码
  2. 工厂办法不会轻易改动的(这个比较重要假如经常改动建议运用工厂办法形式或许笼统工厂)

工厂办法形式

上文也说道了简略工厂形式。简略工厂形式有一个很大的坏处便是不契合开闭准则,那么在运用的时分约束就比较多。工厂办法形式在这方面做了优化,那什么是工厂办法形式呢。

工厂办法形式是指界说一个创立目标的接口,可是完结让这个接口的类来决议实例化那个类,工厂办法让类的实例化推延到了子类中进行。在工厂办法形式中用户只需求联系所需求产品对应的工厂,无需关怀创立的细节,并且加入新的产品契合开闭准则。

通俗点说便是将工厂笼统出来,一个产品类对应的是一个工厂,客户端在需求实例化的时分挑选指定的工厂拿到对应类就可,和简略工厂的差异在于简略工厂只有一个工厂,一切的类的实例化都揉在一个办法里面(当然这儿或许有些人会辩驳我,应为上文中最终一种优化彻底不用写if else运用反射来实例化,可是实践状况或许每品种的实例化都不相同这个时分就不能再运用反射)而工厂办法是多个工厂一个产品对应一个工厂

付出场景(工厂办法完结)

  1. 将创立办法提出来创立一个笼统类
     public interface IPayFactory {
         IPay create();
     }
    
  2. 创立多个工厂
    public class AliPayFactory implements IPayFactory {
      @Override
      public IPay create() {
          return new AliPay();
      }
    }
    
    public class WeChatFactory implements IPayFactory {
        @Override
        public IPay create() {
            return new WeChatPay();
        }
    }
    
    public class UnionPayFactory implements IPayFactory {
        @Override
        public IPay create() {
            return new UnionPay();
        }
    }
    

最终在客户端调用

public class Test {
    public static void main(String[] args) {
        AliPayFactory aliPayFactory = new AliPayFactory();
        aliPayFactory.create().pay("123",100.0);
    }
}

再看一下类图:

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)
此时客户端只需求关怀它需求运用哪个工厂即可,假如需求新增或许删去一个付出办法只需求新增对应的工厂以及详细付出类或许删去对应的即可彻底契合了开闭准则。

相较于简略工厂形式优缺陷

长处:

  1. 契合开闭准则
  2. 工厂责任单一化易于保护

缺陷:

  1. 类变多了(从上文中的类图中就能够显着看出来的缺陷)

适用场景

  1. 创立目标需求许多的重复代码
  2. 客户端不依赖于产品类示例如何被创立、完结等细节

笼统工厂形式

上文说到的工厂办法形式,假如是一个产品族(便是一个工厂不再是单一的只出产一种产品,而是出产多种产品,比如曾经的苹果公司只出产电脑,后来社会在发展苹果公司需求迎合市场的需求开始出产手机,渐渐的又有平板电脑,现在又在造车),运用工厂办法形式就会发现类越来越多,多到不容易保护,难于了解。这个时分就比较合适运用笼统工厂办法。

笼统工厂形式是指供给一个创立一系列相关或许项目依赖目标的接口,无需指定他们的详细类,客户端不依赖于产品类示例如何被创立完结等细节,强调的是一系列相关的产品目标(属于同一产品族)一同运用创立目标需求许多的重复代码,需求供给一个产品类的库,一切的产品以相同的接口出现,从而是客户端不依赖于详细完结

代码示例

就拿上文的苹果公司做示例苹果公司会出产手机平板电脑、电脑、汽车,相同的咱们我国的小米也出产这些产品

  1. 首要将产品族界说一个笼统工厂(也便是说这个产品族能成产那些产品)

      public abstract class CompanyFactory {
          private String companyName;
          public String getCompanyName() {
              return companyName;
          }
          public void init() {
              //这儿能够放一些公共逻辑
              System.out.println("初始化根底数据作业");
          }
          public abstract IPhone createPhone();
          public abstract ICar createCar();
          public abstract IPad createPad();
          public abstract IComputer createComputer();
      }
    

    这儿界说了一个公司的工厂类(并不说出产公司,而是将一切公司能干的工作抽离笼统出来)

  2. 界说每个产品的笼统类

    public interface ICar {
        void running();
    }
    
    public interface IComputer {
        void calculate();
    }
    
    public interface IPad {
        void seeAMovie();
    }
    
    public interface IPhone {
        void listenToMusic();
    }
    
  3. 界说某个公司的工厂及产品完结类 苹果公司的工厂及完结类:

    public class AppleFactory extends CompanyFactory{
       @Override
       public IPhone createPhone() {
           super.init();
           return new ApplePhone();
       }
       @Override
       public ICar createCar() {
           super.init();
           return new AppleCar();
       }
       @Override
       public IPad createPad() {
           super.init();
           return new ApplePad();
       }
       @Override
       public IComputer createComputer() {
           super.init();
           return new AppleComputer();
       }
    }
    
    public class AppleCar implements ICar {
        @Override
        public void running() {
            System.out.println("我正在用苹果公司的汽车拉货");
        }
    }
    
    public class AppleComputer implements IComputer {
        @Override
        public void calculate() {
            System.out.println("我正在用苹果公司的电脑核算");
        }
    }
    
    public class ApplePad implements IPad {
        @Override
        public void seeAMovie() {
            System.out.println("我正在用苹果公司的平板电脑看电影");
        }
    }
    
    public class ApplePhone implements IPhone {
        @Override
        public void listenToMusic() {
            System.out.println("我正在用苹果公司的手机听歌");
        }
    }
    

    小米公司的工厂及完结类

     public class MiuiFactory extends CompanyFactory{
         @Override
         public IPhone createPhone() {
             super.init();
             return new MiuiPhone();
         }
         @Override
         public ICar createCar() {
             super.init();
             return new MiuiCar();
         }
         @Override
         public IPad createPad() {
             super.init();
             return new MiuiPad();
         }
         @Override
         public IComputer createComputer() {
             super.init();
             return new MiuiComputer();
         }
     }
    
    public class MiuiCar implements ICar {
        @Override
        public void running() {
            System.out.println("我正在用小米公司的汽车拉货");
        }
    }
    
    public class MiuiComputer implements IComputer {
        @Override
        public void calculate() {
            System.out.println("我正在用小米公司的电脑核算");
        }
    }
    
    public class MiuiPad implements IPad {
        @Override
        public void seeAMovie() {
            System.out.println("我正在用小米公司的平板电脑看电影");
        }
    }
    
    public class MiuiPhone implements IPhone {
        @Override
        public void listenToMusic() {
            System.out.println("我正在用小米公司的手机听歌");
        }
    }
    
  4. 调用:

    public class Test {
        public static void main(String[] args) {
            CompanyFactory factory = new AppleFactory();
            ICar car = factory.createCar();
            IPad pad = factory.createPad();
            IComputer computer = factory.createComputer();
            IPhone phone = factory.createPhone();
            car.running();
            pad.seeAMovie();
            computer.calculate();
            phone.listenToMusic();
        }
    }
    

至此笼统工厂的举例完结就完结了
咱们能够看一下类图:

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)
大家能够发现假如仍是运用工厂办法规划形式一个产品对应一个工厂办法现在两个公司就对应着2×4个工厂类再增加一个公司的话便是3×4个工厂类这儿有些人或许听不懂为什么是2×4、3×4咱们能够画图暗示一下:
设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)
笼统工厂办法的工厂是按照产品族走的也便是说上图的公司走的,一个工厂能够出产多个产品,所以笼统工厂办法有几个公司就有几个工厂。而工厂办法是跟产品走的,所以有几个产品对应应该有几个工厂。所以相较于工厂办法能够少创立许多类

优缺陷

根据上文中的实例以及类图能够总结如下优缺陷:
长处:

  1. 相比于工厂办法能够少创立许多类

缺陷:

  1. 首要显而易见的是不契合开闭准则,假如增加一个产品那么冲总工厂到详细完结工厂都要增加对应的办法
  2. 增加了系统的笼统性和了解难度

适用场景

其实几个工厂办法运用场景都差不太多,笼统工厂比较合适于大型产品规划,虽然笼统工厂不契合开闭准则,但其实在咱们实践开发中只需不是频频的升级修正也是能够不遵循开闭准则的