3.java设计模式之工厂模式

xiaokantianse 2020-11-11 11:01:32
java 设计 工厂 模式 3.java


基本需求:

  • 一个披萨店需要订购不同种类的披萨

传统方式:

  • 实现思路

    • 在订购类中根据用户不同的输入直接创建不同的披萨实体类进行返回
  • UML类图

  • 代码实现

    • 披萨类

      • // 抽象父类
        public abstract class Pizza {
        String name;
        public abstract void prepare();
        public void bake() {
        System.out.println(this.name + "披萨烘焙中");
        }
        public void cut() {
        System.out.println(this.name + "披萨切割中");
        }
        public void box() {
        System.out.println(this.name + "披萨打包中");
        }
        }
        // 奶酪披萨 子类1
        public class CheesePizza extends Pizza {
        public CheesePizza(String name) {
        this.name = name;
        }
        @Override
        public void prepare() {
        System.out.println(this.name + "披萨准备原材料中");
        }
        }
        // 希腊披萨 子类2
        public class GreekPizza extends Pizza{
        public GreekPizza(String name) {
        this.name = name;
        }
        @Override
        public void prepare() {
        System.out.println(this.name + "披萨准备原材料中");
        }
        }
        
    • 订购类

      • public class PizzaStore {
        public static void main(String[] args) {
        new PizzaStore().OrderPizza();
        }
        // 订购披萨方法 传统方式直接采用new出子类的方法进行返回对应的披萨
        public void OrderPizza() {
        Pizza pizza;
        Scanner scanner = new Scanner(System.in);
        String orderType;
        while (true) {
        System.out.println("输入披萨的种类:");
        orderType = scanner.nextLine();
        if (StringUtils.equals("cheese", orderType)) {
        pizza = new CheesePizza("cheese");
        } else if (StringUtils.equals("greek", orderType)) {
        pizza = new GreekPizza("greek");
        } else {
        System.out.println("没有该类型的披萨");
        break;
        }
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        }
        }
        }
        

缺陷及改进:

  • 在订购方法中,直接采用new出子类的方法进行返回对应的披萨,违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码
  • 如果新增加一种类型的披萨,则需要修改OrderPizza的方法实现,如果有多个创建Pizza的方法则都需要修改
  • 把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可,其它有创建Pizza对象的代码就不需要修改了——>简单工厂模式

简单工厂模式:

  • 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式

  • 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)

  • 当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式

  • 实现思路

    • 创建一个简单工厂类SimpleFactory,提供一个创建Pizza的方法,PizzaStore想要Pizza时,直接在工厂中获取即可
  • UML类图

  • 代码实现

    • // 简单工厂类 Pizza类及实现类同上
      public class SimpleFactory {
      // 创建Pizza的方法 可在多个地方调用,如果需要增加其他种类的Pizza则只需要修改此方法内部的实现即可,不需要调用方
      public Pizza createPizza(String orderType) {
      Pizza pizza = null;
      if (StringUtils.equals("cheese", orderType)) {
      pizza = new CheesePizza("cheese");
      } else if (StringUtils.equals("greek", orderType)) {
      pizza = new GreekPizza("greek");
      } else {
      System.out.println("没有该类型的披萨");
      }
      return pizza;
      }
      // 简单工厂也叫静态工厂 可直接提供静态的方法获取对象
      public static Pizza createPizza1(String orderType) {
      Pizza pizza = null;
      if (StringUtils.equals("cheese", orderType)) {
      pizza = new CheesePizza("cheese");
      } else if (StringUtils.equals("greek", orderType)) {
      pizza = new GreekPizza("greek");
      } else {
      System.out.println("没有该类型的披萨");
      }
      return pizza;
      }
      }
      
    • public class PizzaStore {
      public static void main(String[] args) {
      // new PizzaStore().OrderPizza();
      new PizzaStore(new SimpleFactory());
      }
      private SimpleFactory simpleFactory;
      public PizzaStore(SimpleFactory simpleFactory) {
      this.simpleFactory = simpleFactory;
      OrderPizza();
      }
      private void OrderPizza() {
      Pizza pizza;
      Scanner scanner = new Scanner(System.in);
      String orderType;
      while (true) {
      System.out.println("输入披萨的种类:");
      orderType = scanner.nextLine();
      // 通过Pizza工厂获取Pizza对象
      pizza = simpleFactory.createPizza(orderType);
      if (null == pizza) {
      break;
      }
      pizza.prepare();
      pizza.bake();
      pizza.cut();
      pizza.box();
      }
      }
      }
      

工厂方法模式:

  • 在上面的基础上增加了新的需求,需要在每种pizza上加上产地,例如 北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza

  • 思路1:可以使用简单工厂模式,新建两个简单工厂,BJPizzaSimpleFactory和LDPizzaSimpleFactory 但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好

  • 思路2:采用工厂方法模式

    • 将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现
    • 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
  • 实现思路

    • 在PizzaStore中增加一个创建Pizza抽象方法,由BJPizzaStore和LDPizzaStore去实现,至此该两个子类创建的Pizza都是各自地区的
  • UML类图

  • 代码实现

    • // 含有抽象方法的抽象类
      public abstract class PizzaStore {
      public static void main(String[] args) {
      // 根据地区选工厂? 没品出来和创建多个简单工厂有什么好处
      String area = "BJ";
      if (area.equals("BJ")) {
      new BJPizzaStore();
      } else {
      new LDPizzaStore();
      }
      }
      public PizzaStore() {
      OrderPizza();
      }
      private void OrderPizza() {
      Pizza pizza;
      Scanner scanner = new Scanner(System.in);
      String orderType;
      while (true) {
      System.out.println("输入披萨的种类:");
      orderType = scanner.nextLine();
      // 使用抽象方法来创建Pizza,使用不同的实现类将会得到不同的披萨
      pizza = createPizza(orderType);
      if (null == pizza) {
      break;
      }
      pizza.prepare();
      pizza.bake();
      pizza.cut();
      pizza.box();
      }
      }
      // 创建Pizza的抽象方法 由子类实现
      public abstract Pizza createPizza(String orderType);
      }
      
    • // 子类1
      public class BJPizzaStore extends PizzaStore {
      @Override
      public Pizza createPizza(String orderType) {
      Pizza pizza = null;
      if (StringUtils.equals("cheese", orderType)) {
      pizza = new BJCheesePizza("BJCheese");
      } else if (StringUtils.equals("pepper", orderType)) {
      pizza = new BJPepperPizza("BJPepper");
      } else {
      System.out.println("没有该类型的披萨");
      }
      return pizza;
      }
      }
      // 子类2
      public class LDPizzaStore extends PizzaStore {
      @Override
      public Pizza createPizza(String orderType) {
      Pizza pizza = null;
      if (StringUtils.equals("cheese", orderType)) {
      pizza = new BJCheesePizza("LDCheese");
      } else if (StringUtils.equals("pepper", orderType)) {
      pizza = new BJPepperPizza("LDPepper");
      } else {
      System.out.println("没有该类型的披萨");
      }
      return pizza;
      }
      }
      

抽象工厂模式:

  • 抽象工厂模式定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类,可以将简单工厂模式和工厂方法模式进行整合

  • 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象),将工厂抽象成两层,

  • 实现思路

    • 创建AbsFactory( 抽象工厂) 和具体实现的工厂子类,使用时创建工厂子类即可
  • UML类图

  • 代码实现

    • // 抽象工厂类
      public interface AbsFactory {
      Pizza createPizza(String orderType);
      }
      // 子类1
      public class BJFactory implements AbsFactory {
      @Override
      public Pizza createPizza(String orderType) {
      Pizza pizza = null;
      if (StringUtils.equals("cheese", orderType)) {
      pizza = new BJCheesePizza("BJCheese");
      } else if (StringUtils.equals("pepper", orderType)) {
      pizza = new BJPepperPizza("BJPepper");
      } else {
      System.out.println("没有该类型的披萨");
      }
      return pizza;
      }
      }
      // 子类2
      public class LDFactory implements AbsFactory {
      @Override
      public Pizza createPizza(String orderType) {
      Pizza pizza = null;
      if (StringUtils.equals("cheese", orderType)) {
      pizza = new BJCheesePizza("LDCheese");
      } else if (StringUtils.equals("pepper", orderType)) {
      pizza = new BJPepperPizza("LDPepper");
      } else {
      System.out.println("没有该类型的披萨");
      }
      return pizza;
      }
      }
      
    • public class PizzaStore {
      public static void main(String[] args) {
      new PizzaStore(new BJFactory());
      }
      private AbsFactory factory;
      public PizzaStore(AbsFactory factory) {
      // 在此处声明需要工厂类对象 , 使用时直接创建抽象工厂的子类对象即可
      this.factory = factory;
      OrderPizza();
      }
      private void OrderPizza() {
      Pizza pizza;
      Scanner scanner = new Scanner(System.in);
      String orderType;
      while (true) {
      System.out.println("输入披萨的种类:");
      orderType = scanner.nextLine();
      // 使用抽象方法来创建Pizza,使用不同的实现类将会得到不同的披萨
      pizza = factory.createPizza(orderType);
      if (null == pizza) {
      break;
      }
      pizza.prepare();
      pizza.bake();
      pizza.cut();
      pizza.box();
      }
      }
      }
      

jdk源码:

  • 在Calendar类中的getInstance()方法中有用到简单工厂模式

  • public static Calendar getInstance()
    {
    // 获取默认的zone和aLocale
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }
    public static Calendar getInstance(TimeZone zone,
    Locale aLocale)
    {
    return createCalendar(zone, aLocale);
    }
    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;
    // 此处使用了简单工厂模式来创建Calender对象 通过aLocale不同的后缀
    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;
    }
    }
    }
    ......
    return cal;
    }
    

注意事项:

  • 意义:将实例化对象的的代码抽取出来,放到一个类中同一管理和维护,在项目中达到依赖解耦的目的,提高项目的扩展性和维护性
  • 依赖抽象原则(尽量依赖抽象的东西)
    • 创建对象时,不要使用new,而是将new的东西放在一个工厂中并返回
    • 不要让类继承一个具体的类,而是继承抽象类或者实现接口,不要覆盖基类汇总已经实现的方法
版权声明
本文为[xiaokantianse]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/xiaokantianse/p/13957494.html

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云