• 软件工程——设计模式之创建型模式(单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。)


    目录

    设计模式的六大原则

    单例模式

    抽象工厂模式

    建造者模式

    工厂模式

    原型模式

    设计模式的六大原则

    1. 开闭原则(Open Close Principle):开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
    2. 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
    3. 依赖倒转原则(Dependence Inversion Principle:这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
    4. 接口隔离原则(Interface Segregation Principle):这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
    5. 迪米特法则(最少知道原则)(Demeter Principle):为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
    6. 合成复用原则(Composite Reuse Principle):原则是尽量使用合成/聚合的方式,而不是使用继承

    创建型模式

    单例模式

    单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例

    单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例

    • 定义:只需要三步就可以保证对象的唯一性

      • (1) 不允许其他程序用new对象
      • (2) 在该类中创建对象
      • (3) 对外提供一个可以让其他程序获取该对象的方法
    • 对比定义:

      • (1) 私有化该类的构造函数
      • (2) 通过new在本类中创建一个本类对象
      • (3) 定义一个公有的方法,将在该类中所创建的对象返回
    • 饿汉式[可用]:SingletonEHan.java

    1. /**
    2. * Created by jingbin on 2016/10/27.
    3. * 1.单例模式的饿汉式[可用]
    4. * (1)私有化该类的构造函数
    5. * (2)通过new在本类中创建一个本类对象
    6. * (3)定义一个公有的方法,将在该类中所创建的对象返回
    7. *

    8. * 优点:从它的实现中我们可以看到,这种方式的实现比较简单,在类加载的时候就完成了实例化,避免了线程的同步问题。
    9. * 缺点:由于在类加载的时候就实例化了,所以没有达到Lazy Loading(懒加载)的效果,也就是说可能我没有用到这个实例,但是它
    10. * 也会加载,会造成内存的浪费(但是这个浪费可以忽略,所以这种方式也是推荐使用的)。
    11. */
    12. public class SingletonEHan {
    13. private SingletonEHan() {}
    14. /**
    15. * 1.单例模式的饿汉式[可用]
    16. */
    17. private static SingletonEHan singletonEHan = new SingletonEHan();
    18. public static SingletonEHan getInstance() {
    19. return singletonEHan;
    20. }
    21. // SingletonEHan instance= SingletonEHan.getInstance();
    22. /**
    23. * 2. 单例模式的饿汉式变换写法[可用]
    24. * 基本没区别
    25. */
    26. private static SingletonEHan singletonEHanTwo = null;
    27. static {
    28. singletonEHanTwo = new SingletonEHan();
    29. }
    30. public static SingletonEHan getSingletonEHan() {
    31. if (singletonEHanTwo == null) {
    32. singletonEHanTwo = new SingletonEHan();
    33. }
    34. return singletonEHanTwo;
    35. }
    36. // SingletonEHan instance= SingletonEHan.getSingletonEHan();
    37. }
    1. private SingletonLanHan() {}
    2. private static SingletonLanHan singletonLanHanFour;
    3. public static SingletonLanHan getSingletonLanHanFour() {
    4. if (singletonLanHanFour == null) {
    5. synchronized (SingletonLanHan.class) {
    6. if (singletonLanHanFour == null) {
    7. singletonLanHanFour = new SingletonLanHan();
    8. }
    9. }
    10. }
    11. return singletonLanHanFour;
    12. }
    1. /**
    2. * Created by jingbin on 2016/10/28.
    3. * 7. 内部类[推荐用]
    4. *

    5. * 这种方式跟饿汉式方式采用的机制类似,但又有不同。
    6. * 两者都是采用了类装载的机制来保证初始化实例时只有一个线程。
    7. * 不同的地方:
    8. * 在饿汉式方式是只要Singleton类被装载就会实例化,
    9. * 内部类是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类
    10. *

    11. * 优点:避免了线程不安全,延迟加载,效率高。
    12. */
    13. public class SingletonIn {
    14. private SingletonIn() {
    15. }
    16. private static class SingletonInHodler {
    17. private static SingletonIn singletonIn = new SingletonIn();
    18. }
    19. public static SingletonIn getSingletonIn() {
    20. return SingletonInHodler.singletonIn;
    21. }
    22. }
    1. /**
    2. * Created by jingbin on 2016/10/28.
    3. * 8. 枚举[极推荐使用]
    4. *
    5. * 这里SingletonEnum.instance
    6. * 这里的instance即为SingletonEnum类型的引用所以得到它就可以调用枚举中的方法了。
    7. 借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
    8. */
    9. public enum SingletonEnum {
    10. instance;
    11. private SingletonEnum() {
    12. }
    13. public void whateverMethod() {
    14. }
    15. // SingletonEnum.instance.method();
    16. }

    建造者模式

    建造模式是对象的创建模式。建造模式可以将一个产品的内部表象(internal representation)与产品的生产过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。

    • 需求:用户去汽车店购买汽车。

    • 分析:汽车店根据每个用户的需求提取对应汽车

    • 建造者超类:Builder

    1. public abstract class Builder {
    2. public abstract void setPart(String name, String type);
    3. public abstract Product getProduct();
    4. }
    • 建造者对应实现类:ConcreteBuilder

      1. public class ConcreteBuilder extends Builder {
      2. private Product product = new Product();
      3. @Override
      4. public void setPart(String name, String type) {
      5. product.setName(name);
      6. product.setType(type);
      7. }
      8. @Override
      9. public Product getProduct() {
      10. return product;
      11. }
      12. }
    • 店长Director取汽车:

    1. // 店长
    2. Director director = new Director();
    3. // 得到宝马汽车,内部实现提取宝马汽车的详情操作
    4. Product product = director.getBProduct();
    5. // 展示汽车信息
    6. product.showProduct();

    工厂模式

    简单列一下这个模式的家族:

    • 1、静态工厂模式

      • 这个最常见了,项目中的辅助类,TextUtil.isEmpty等,类+静态方法。
    • 2、简单工厂模式(店里买肉夹馍)

      • 定义:通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
      • 根据类型直接创建肉夹馍:SimpleRoujiaMoFactory.java
    1. /**
    2. * Created by jingbin on 2016/10/23.
    3. * 简单工厂模式
    4. */
    5. public class SimpleRoujiaMoFactory {
    6. public RoujiaMo creatRoujiaMo(String type) {
    7. RoujiaMo roujiaMo = null;
    8. switch (type) {
    9. case "Suan":
    10. roujiaMo = new ZSuanRoujiaMo();
    11. break;
    12. case "La":
    13. roujiaMo = new ZLaRoujiaMo();
    14. break;
    15. case "Tian":
    16. roujiaMo = new ZTianRoujiaMo();
    17. break;
    18. default:// 默认为酸肉夹馍
    19. roujiaMo = new ZSuanRoujiaMo();
    20. break;
    21. }
    22. return roujiaMo;
    23. }
    24. }

     

    1. public RoujiaMo creatRoujiaMo(String type) {
    2. RoujiaMo roujiaMo = null;
    3. switch (type) {
    4. case "Suan":
    5. roujiaMo = new ZSuanRoujiaMo();
    6. break;
    7. case "La":
    8. roujiaMo = new ZLaRoujiaMo();
    9. break;
    10. case "Tian":
    11. roujiaMo = new ZTianRoujiaMo();
    12. break;
    13. default:// 默认为酸肉夹馍
    14. roujiaMo = new ZSuanRoujiaMo();
    15. break;
    16. }
    17. return roujiaMo;
    18. }
    • 3、工厂方法模式(开分店)

      • 定义:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式把类实例化的过程推迟到子类。
      • 对比定义:
      • 1、定义了创建对象的一个接口:public abstract RouJiaMo sellRoujiaMo(String type);
      • 2、由子类决定实例化的类,可以看到我们的馍是子类生成的。
    • 提供创建肉夹馍店抽象方法:RoujiaMoStore.java

    1. /**
    2. * Created by jingbin on 2016/10/24.
    3. *
    4. * 在北京和西安 开分店:
    5. * 工厂方法模式:
    6. * 定义:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。
    7. * 工厂方法模式把类实例化的过程推迟到子类。
    8. *
    9. * 对照定义:
    10. 1、定义了创建对象的一个接口:public abstract RouJiaMo sellRoujiaMo(String type);
    11. 2、由子类决定实例化的类,可以看到我们的馍是子类生成的。
    12. */
    13. public abstract class RoujiaMoStore {
    14. public abstract RoujiaMo sellRoujiaMo(String type);
    15. // public RoujiaMo sellRoujiaMo(String type) {
    16. //
    17. // RoujiaMo roujiaMo = creatRoujiaMo(type);
    18. // roujiaMo.prepare();
    19. // roujiaMo.fire();
    20. // roujiaMo.pack();
    21. // return roujiaMo;
    22. //
    23. // }
    24. }
    public abstract RoujiaMo sellRoujiaMo(String type);
    1. /**
    2. * Created by jingbin on 2016/10/24.
    3. * 西安肉夹馍店 让分店自己去卖自己口味的肉夹馍
    4. */
    5. public class XianRoujiaMoStore extends RoujiaMoStore {
    6. private XianSimpleRoujiaMoFactory factory;
    7. public XianRoujiaMoStore(XianSimpleRoujiaMoFactory factory) {
    8. this.factory = factory;
    9. }
    10. public RoujiaMo sellRoujiaMo(String type) {
    11. RoujiaMo roujiaMo = factory.creatRoujiaMo(type);
    12. roujiaMo.prepare();
    13. roujiaMo.fire();
    14. roujiaMo.pack();
    15. return roujiaMo;
    16. }
    17. // @Override
    18. // public RoujiaMo creatRoujiaMo(String type) {
    19. //
    20. // RoujiaMo roujiaMo = null;
    21. // switch (type) {
    22. // case "suan":
    23. // roujiaMo = new XianSuanRoujiMo();
    24. // break;
    25. // case "tian":
    26. // roujiaMo = new XianKuRoujiMo();
    27. // break;
    28. // case "la":
    29. // roujiaMo = new XianlaRoujiMo();
    30. // break;
    31. // default:// 默认为 西安 酸肉夹馍
    32. // roujiaMo = new XianSuanRoujiMo();
    33. // break;
    34. // }
    35. // return roujiaMo;
    36. // }
    37. }
    1. /**
    2. * Created by jingbin on 2016/10/23.
    3. * 西安 简单工厂模式:
    4. * 用来西安店生产自己店里的肉夹馍
    5. */
    6. public class XianSimpleRoujiaMoFactory {
    7. public RoujiaMo creatRoujiaMo(String type) {
    8. RoujiaMo roujiaMo = null;
    9. switch (type) {
    10. case "Suan":
    11. roujiaMo = new XianSuanRoujiMo();
    12. break;
    13. case "La":
    14. roujiaMo = new XianKuRoujiMo();
    15. break;
    16. case "Tian":
    17. roujiaMo = new XianlaRoujiMo();
    18. break;
    19. default:// 默认为酸肉夹馍
    20. roujiaMo = new XianSuanRoujiMo();
    21. break;
    22. }
    23. return roujiaMo;
    24. }
    25. }

    抽象工厂模式

    • 定义:提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类。
    • 对比定义:
      • 1、提供一个接口:public interface RouJiaMoYLFactroy
      • 2、用于创建相关的或依赖对象的家族 public Meat createMeat();public YuanLiao createYuanliao();我们接口用于创建一系列的原材料。
    • 创建用于提供原料的接口工厂:RoujiaMoYLFactory.java
    1. /**
    2. * Created by jingbin on 2016/10/26.
    3. * 4、抽象工厂模式:
    4. * 定义:提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类。
    5. * 这定义有点绕口,算了,还是拿例子来说。
    6. * 继续卖肉夹馍,咱们生意这么好,难免有些分店开始动歪脑子,开始使用劣质肉等,砸我们的品牌。
    7. * 所以我们要拿钱在每个城市建立自己的原料场,保证高质量原料的供应。
    8. */
    9. public interface RoujiaMoYLFactory {
    10. /**
    11. * 生产肉
    12. */
    13. public Meet creatMeet();
    14. /**
    15. * 生产一些原料
    16. */
    17. public YuanLiao creatYuanLiao();
    18. }
    1. /**
    2. * Created by jingbin on 2016/10/26.
    3. * 西安的肉夹馍原料工厂,是西安的特色原料,还有其他分店的特色原料
    4. */
    5. public class XianRoujiaMoYLFoctory implements RoujiaMoYLFactory {
    6. @Override
    7. public Meet creatMeet() {
    8. return new XianFreshMeet();
    9. }
    10. @Override
    11. public YuanLiao creatYuanLiao() {
    12. return new XianTeSeYuanLiao();
    13. }
    14. }
    1. import android.util.Log;
    2. /**
    3. * Created by jingbin on 2016/10/22.
    4. */
    5. public abstract class RoujiaMo {
    6. protected String name;
    7. /**
    8. * 准备工作
    9. */
    10. public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
    11. Meet meet = roujiaMoYLFactory.creatMeet();
    12. YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
    13. Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成准备工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
    14. }
    15. /**
    16. * 秘制设备--烘烤2分钟
    17. */
    18. public void fire() {
    19. Log.e("---RoujiaMo:", name + ": 肉夹馍-专用设备-烘烤");
    20. }
    21. /**
    22. * 使用你们的专用袋-包装
    23. */
    24. public void pack() {
    25. Log.e("---RoujiaMo:", name + ": 肉夹馍-专用袋-包装---end");
    26. }
    27. }
    1. /**
    2. * 准备工作
    3. */
    4. public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
    5. Meet meet = roujiaMoYLFactory.creatMeet();
    6. YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
    7. Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成准备工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
    8. }

    原型模式

    原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

    以获取多种形状为例,共分四步:

    • 1、创建一个实现了 Cloneable 接口的抽象类。Shape(implements Cloneable)

    1. public abstract class Shape implements Cloneable {
    2. private String id;
    3. protected String type;
    4. public abstract void draw();
    5. public String getId() {
    6. return id;
    7. }
    8. public void setId(String id) {
    9. this.id = id;
    10. }
    11. @Override
    12. public Object clone() {
    13. Object object = null;
    14. try {
    15. object = super.clone();
    16. } catch (CloneNotSupportedException e) {
    17. Log.e("--", e.getMessage());
    18. }
    19. return object;
    20. }
    21. }

    2、创建扩展了上面抽象类的实体类。CircleRectangleSquare

    1. public class Circle extends Shape {
    2. public Circle() {
    3. type = "Circle";
    4. }
    5. @Override
    6. public void draw() {
    7. Log.e("---", "Inside Circle::draw() method.");
    8. }
    9. }
    1. /**
    2. * Created by jingbin on 2020-01-31.
    3. * 2. 创建扩展了上面抽象类的实体类。Rectangle 矩形
    4. */
    5. public class Rectangle extends Shape {
    6. public Rectangle() {
    7. type = "Rectangle";
    8. }
    9. @Override
    10. public void draw() {
    11. Log.e("---", "Inside Rectangle::draw() method.");
    12. }
    13. }
    1. /**
    2. * Created by jingbin on 2020-01-31.
    3. * 2. 创建扩展了上面抽象类的实体类。Square 正方形
    4. */
    5. public class Square extends Shape {
    6. public Square() {
    7. type = "Square";
    8. }
    9. @Override
    10. public void draw() {
    11. Log.e("---", "Inside Square::draw() method.");
    12. }
    13. }

    3、创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。ShapeCache

    1. public class ShapeCache {
    2. private static Hashtable shapeMap = new Hashtable();
    3. public static Shape getShape(String shapeId) {
    4. Shape shapeCache = shapeMap.get(shapeId);
    5. return (Shape) shapeCache.clone();
    6. }
    7. // 对每种形状都运行数据库查询,并创建该形状
    8. // shapeMap.put(shapeKey, shape);
    9. // 例如,我们要添加三种形状
    10. public static void loadCache() {
    11. Circle circle = new Circle();
    12. circle.setId("1");
    13. shapeMap.put(circle.getId(), circle);
    14. Rectangle rectangle = new Rectangle();
    15. rectangle.setId("2");
    16. shapeMap.put(rectangle.getId(), rectangle);
    17. Square square = new Square();
    18. square.setId("3");
    19. shapeMap.put(square.getId(), square);
    20. }
    21. }

    4、使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。

    1. // 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。
    2. ShapeCache.loadCache();
    3. Shape shapeCache1 = ShapeCache.getShape("1");
    4. Shape shapeCache2 = ShapeCache.getShape("2");
    5. Shape shapeCache3 = ShapeCache.getShape("3");

    参考材料

    相关代码github地址:GitHub - youlookwhat/DesignPattern: 📚 Java 23种设计模式全归纳

    相关文章:Java 23种设计模式全归纳 | 完结版-腾讯云开发者社区-腾讯云 (tencent.com)

  • 相关阅读:
    猿创征文|【HTML】标签学习之路
    ubuntu18.04 安装网卡i219-LM驱动
    在Visual Studio中部署GDAL库的C++版本(包括SQLite、PROJ等依赖)
    网络工程师干货:华为设备故障管理命令大全
    通信原理学习笔记6-4:数字解调——抽样判决的译码准则(最大后验概率准则MAP、最大似然准则ML、最小二乘/最小平方准则LS、最小距离准则)
    BATJ和字节跳动这些大厂的内部面试解析,面试重难点超出你的想象
    Rust语言基础:从Hello World开始
    【RocketMQ】消息的拉取
    Nacos 开源版的使用测评
    One bite of Stream(6)
  • 原文地址:https://blog.csdn.net/batcat560/article/details/137522600