Java设计模式——工厂模式讲解以及在JDK中源码分析

2023-07-16 14:51:46 浏览数 (2)

小熊学Java个人网站:https://javaxiaobear.gitee.io/

需求:便于手机种类的扩展

  • 手机的种类很多(比如HuaWeiPhone、XiaoMiPhone等)
  • 手机的制作有prepare,production, assemble, box
  • 完成手机店订购功能。

1、传统模式

手机抽象类

代码语言:javascript复制
 public abstract class Phone {
 
     private String name;
 
     /**
      * 准备的抽象类
      */
     public abstract void prepare();
 
     /**
      * 手机生产
      */
     public void production(){
         System.out.println(name   "手机production");
     }
 
     /**
      * 手机组装
      */
     public void assemble(){
         System.out.println(name   "手机assemble");
     }
 
     /**
      * 手机打包
      */
     public void box(){
         System.out.println(name   "手机box");
     }
 
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this.name = name;
     }
 }

实体类

代码语言:javascript复制
 public class HuaWeiPhone extends Phone{
     @Override
     public void prepare() {
         System.out.println("准备华为手机原材料!");
     }
 }
代码语言:javascript复制
 public class XiaoMiPhone extends Phone{
 
     @Override
     public void prepare() {
         System.out.println("准备小米手机原材料!");
     }
 }

基于方法实现

代码语言:javascript复制
 public class OrderPhone {
 
     public OrderPhone() {
         Phone phone;
         String orderType;
         while (true){
             orderType = inputType();
             if (orderType.equals("huawei")){
                 phone = new HuaWeiPhone();
                 phone.setName("华为");
             }else if(orderType.equals("xiaomi")){
                 phone = new XiaoMiPhone();
                 phone.setName("小米");
             }else {
                 break;
             }
             phone.prepare();
             phone.production();
             phone.assemble();
             phone.box();
         }
     }
 
     /**
      * 键盘输入类型
      * @return
      */
     public String inputType(){
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入手机类型:");
         String next = scanner.next();
         return next;
     }
 }

调用

代码语言:javascript复制
 public class PhoneStore {
 
     public static void main(String[] args) {
         new OrderPhone();
     }
 }

传统方法的优缺点:

优点:比较好理解,简单易操作。 缺点:违反了设计模式的ocp 原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码. 比如我们这时要新增加一个Phone的种类(oppo Phone),就需要增加OPPOPhone类,同时也要修改OrderPhone类

改进的思路:

修改代码可以接受,但是如果我们在其它的地方也有创建Phone的代码,就意味着,也需要修改,而创建Phone的代码,往往有多处。 思路:把创建Phone对象封装到一个类中,这样我们有新的Phone种类时,只需要修改该类就可,其它有创建到Phone对象的代码就不需要修改了=> 简单工厂模式

2、简单工厂模式

1、介绍

简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码) 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式.

2、代码实现

新建SimpleFactory

代码语言:javascript复制
 public class SimpleFactory {
 
     public Phone createPhone(String orderType) {
         Phone phone = null;
         if (orderType.equals("huawei")){
             phone = new HuaWeiPhone();
             phone.setName("华为");
         }else if(orderType.equals("xiaomi")){
             phone = new XiaoMiPhone();
             phone.setName("小米");
         }
         return phone;
     }
 }

修改订购手机类OrderPhone

代码语言:javascript复制
 public class OrderPhone {
     /**
      * 键盘输入类型
      * @return
      */
     public String inputType(){
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入手机类型:");
         String next = scanner.next();
         return next;
     }
 
     SimpleFactory simpleFactory;
     Phone phone = null;
 
 
     public OrderPhone(SimpleFactory simpleFactory){
         setSimpleFactory(simpleFactory);
     }
 
     public void setSimpleFactory(SimpleFactory simpleFactory){
         String orderType;
         //设置简单工厂对象
        this.simpleFactory = simpleFactory;
        do {
            orderType = inputType();
            phone = this.simpleFactory.createPhone(orderType);
            if (phone != null) {
                phone.prepare();
                phone.production();
                phone.assemble();
                phone.box();
            }else {
                System.out.println("订购失败");
                break;
            }
        }while (true);
     }
 }

修改调用类

代码语言:javascript复制
 public class PhoneStore {
 
     public static void main(String[] args) {
 //        new OrderPhone();
         new OrderPhone(new SimpleFactory());
     }
 }

3、工厂方法模式

新的需求:

订购不同种类的、不同厂家的手机

思路

  1. 使用简单工厂模式,创建不同的远程工厂,这样也是可以的,之前简单工厂模式就实现了,但考虑到项目的规模,以及软件的维护性,可扩展性并不是特别好
  2. 使用工厂方法模式
1、工厂方法模式介绍

定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

思路:

将创建的过程写成抽象类和抽象方法

步骤

将订购手机的类变成抽象类,抽象方法为创建手机

代码语言:javascript复制
 public abstract class OrderPhone {
 
     /**
      * 键盘输入类型
      * @return
      */
     public String inputType(){
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入手机类型:");
         String next = scanner.next();
         return next;
     }
 
     abstract Phone createPhone(String orderType);
     
     public OrderPhone(){
         Phone phone;
         String orderType;
         do {
             orderType = inputType();
             //抽象方法,由工厂子类完成
             phone = createPhone(orderType);
             phone.prepare();
             phone.production();
             phone.assemble();
             phone.box();
         }while (true);
     }
 
     public static void main(String[] args) {
         new DomesticPhone();
     }
 
 }

分类手机,继承抽象类

代码语言:javascript复制
 public class DomesticPhone extends OrderPhone{
     @Override
     Phone createPhone(String orderType) {
         Phone phone = null;
         if (orderType.equals("huawei")){
             phone = new HuaWeiPhone();
             phone.setName("华为");
         }else if (orderType.equals("xiaomi")){
             phone = new XiaoMiPhone();
             phone.setName("小米");
         }
         return phone;
     }
 }
代码语言:javascript复制
 public class IPhone extends OrderPhone{
     @Override
     Phone createPhone(String orderType) {
         Phone phone = null;
         if (orderType.equals("pingguo")){
             phone = new PingGuoPhone();
             phone.setName("苹果");
         }else if (orderType.equals("sanxing")){
             phone = new SanXingPhone();
             phone.setName("三星");
         }
         return phone;
     }
 }

4、抽象工厂模式

定义了一个interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。 将工厂抽象成两层,AbsFactory(抽象工厂) 和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

定义接口,让子类工厂进行实现

代码语言:javascript复制
 public interface AbstractFactory {
 
     /**
      * 建Phone,让工厂子类实现
      * @param orderType 类型
      * @return  phone
      */
     public Phone createPhone(String orderType);
 }

子类工厂实现接口

代码语言:javascript复制
 public class DomesticPhoneFactory implements AbstractFactory {
 
     @Override
     public Phone createPhone(String orderType) {
         Phone phone = null;
         System.out.println("~使用的是抽象工厂模式~");
         if (orderType.equals("huawei")){
             phone = new HuaWeiPhone();
             phone.setName("华为");
         }else if (orderType.equals("xiaomi")){
             phone = new XiaoMiPhone();
             phone.setName("小米");
         }
         return phone;
     }
 }
代码语言:javascript复制
 public class ForeignPhoneFactory implements AbstractFactory {
 
     @Override
     public Phone createPhone(String orderType) {
         Phone phone = null;
         if (orderType.equals("pingguo")){
             phone = new PingGuoPhone();
             phone.setName("苹果");
         }else if (orderType.equals("sanxing")){
             phone = new SanXingPhone();
             phone.setName("三星");
         }
         return phone;
     }
 }

具体调用实现

代码语言:javascript复制
 public class OrderPhone {
 
     AbstractFactory factory;
 
     /**
      * 键盘输入类型
      * @return
      */
     public String inputType(){
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入手机类型:");
         String next = scanner.next();
         return next;
     }
 
     public OrderPhone(AbstractFactory factory){
         setFactory(factory);
     }
 
 
     private void setFactory(AbstractFactory factory){
         Phone phone;
         String orderType;
         this.factory = factory;
         do {
             orderType = inputType();
             //抽象工厂方法
             phone = factory.createPhone(orderType);
             if (phone != null) {
                 phone.prepare();
                 phone.production();
                 phone.assemble();
                 phone.box();
             }else {
                 System.out.println("订购失败!");
                 break;
             }
         }while (true);
     }
 
     public static void main(String[] args) {
         new OrderPhone(new DomesticPhoneFactory());
     }
 }

5、工厂模式在JDK-Calendar 应用的源码分析

代码语言:javascript复制
 public class FactoryTest {
 
     public static void main(String[] args) {
 
         Calendar cal = Calendar.getInstance();
         // 注意月份下标从0 开始,所以取月份要 1
         System.out.println("年:"   cal.get(Calendar.YEAR));
         System.out.println("月:"   (cal.get(Calendar.MONTH)   1));
         System.out.println("日:"   cal.get(Calendar.DAY_OF_MONTH));
         System.out.println("时:"   cal.get(Calendar.HOUR_OF_DAY));
         System.out.println("分:"   cal.get(Calendar.MINUTE));
         System.out.println("秒:"   cal.get(Calendar.SECOND));
     }
 }
代码语言:javascript复制
 public static Calendar getInstance()
 {
     return createCalendar(TimeZone.getDefault(), 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) {
                 switch (caltype) {
                 case "buddhist":
                 cal = new BuddhistCalendar(zone, aLocale);
                     break;
                 case "japanese":
                     cal = new JapaneseImperialCalendar(zone, aLocale);
                     break;
                 case "gregory":
                     cal = new GregorianCalendar(zone, aLocale);
                     break;
                 }
             }
         }
         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;
     }

6、工厂模式小结

意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。 三种工厂模式:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

涉及到设计模式的依赖抽象原则 创建对象实例时,不要直接new 类, 而是把这个new 类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。 不要让类继承具体类,而是继承抽象类或者是实现interface(接口),不要覆盖基类中已经实现的方法。

如果觉得内容不错的话,希望大家可以帮忙点赞转发一波,这是对我最大的鼓励,感谢

0 人点赞