Java笔记19 - 设计模式 - 创建性模式

  • 使用设计模式的目的是为了可重用代码, 提高代码的可扩展性和可维护性

  • 尽量复用代码, 降低代码的偶合度

  • 设计模式主要是基于OOP编程提炼的

  • 开闭原则: 尽量对扩展开发, 对修改关闭. 能不修改代码, 只增加代码即可完成新功能, 是最好的.

  • 里氏替换原则: 如果我们调用父类的方法可以成功, 替换成子类调用也可以成功

  • 创建性模式
  • 关注如何创建对象, 核心是把对象的创建和使用分开, 两者可以相对独立的变换

工厂方法

定义一个用于创建对象的接口, 让子类决定实例化哪一个类. 工厂方法是: 使一个类的创建延迟到子类

  • 工厂方法目的: 使得创建对象和使用对象是分离的, 并且客户端总是引用抽象工场和抽象产品.
public class Main {
  public static void main(String[] args) {
    NumberFactory factory = NumberFactory.getFactory();
    Number result = factory.parse("1234");
    System.out.println(result);
  }
}

// 实现一个解析字符串到`Number`的Factory
interface NumberFactory {
  Number parse(String s);
  // 获取工厂实例
  static NumberFactory getFactory() {
    return impl;
  }
  static NumberFactory impl = new NumberFactoryImpl();
}

// 工厂的实现类
class NumberFactoryImpl implements NumberFactory {
  public Number parse(String s) {
    return new BigDecimal(s);
  }
}
  • 可以完全忽略真正的工程NumberFactoryImpl和产品BigDecimal
  • 允许创建产品的代码独立变化, 而不会影响到调用方
interface NumberFactory {
  public static Number parse(String s) {
    return new BigDecimal(s);
  }
}
  • 静态工厂方法

  • 工厂方法可以隐藏创建产品的细节, 且不会每一次都会真正的创建的产品. 也可能是从缓存中拿出, 从而提升速度并减少内存消耗

  • 例如Integer.valueOf(), 可以提前初始化很多常用的, 实例, 每次都调用缓存中的实例, 当没有的时候, 再放一个进去

  • 总是引用接口, 能允许变化子类, 而不影响调用方, 尽可能面向对象编程

// TODO: 没搞明白List.of()是如何执行并返回的.
List<String> list = List.of("a", "b", "c", "d");
System.out.println(list);
class LocalDateFactory {
  private static Map<Integer, LocalDate> cache = new HashMap<Integer, LocalDate>();

  public static LocalDate fromIn(int yyyyMMdd) {
    if (yyyyMMdd > 20000101 && yyyyMMdd < 20200101) {
      if (cache.containsKey(yyyyMMdd)) return cache.get(yyyyMMdd);
      LocalDate value = createDate(yyyyMMdd);
      cache.put(yyyyMMdd, value);
      return value;
    } else {
      return createDate(yyyyMMdd);
    }
  }

  private static LocalDate createDate(int yyyyMMdd) {
    System.out.println("run createDate");
    return LocalDate.of(yyyyMMdd / 1000, yyyyMMdd / 100 % 100, yyyyMMdd % 100);
  }
}
  • 产生实例的过程外界并不知道, 可以进行适当的缓存, 增加产生实例的效率

抽象工厂

提供一个创建一系列相关或者相互依赖对象的接口, 而无需指定他们具体的类

  • 工厂是抽象的, 工厂产出的产品也是抽象的.
  • 然后有个具体的工厂1, 实现了工厂, 工厂1中的产品1-1, 实现了产品
├── lib
└── src
    ├── App.java
    ├── fastfactory
    │   ├── FastFactory.java // 实际工厂
    │   ├── FastHtmlDocument.java // 实际产品
    │   └── FastWordDocument.java // 实际产品
    ├── goodfactory
    │   ├── GoodFactory.java // 实际工厂
    │   ├── GoodHtmlDocument.java // 实际产品
    │   └── GoodWordDocument.java // 实际产品
    └── services
        ├── AbstractFactory.java // 虚拟工厂
        ├── HtmlDocument.java // 虚拟产品
        └── WordDocument.java // 虚拟产品
// 虚拟工厂
public interface AbstractFactory {
  HtmlDocument createHtml(String md);

  WordDocument createWord(String md);
}
// 虚拟产品
public interface HtmlDocument {
  String toHtml();

  void save(Path path);
}
// 虚拟产品
public interface WordDocument {
  void save(Path path);
}
// 实际工厂
public class GoodFactory implements AbstractFactory {

  @Override
  public HtmlDocument createHtml(String md) {
    return new GoodHtmlDocument(md);
  }

  @Override
  public WordDocument createWord(String md) {
    return new GoodWordDocument(md);
  }

}
// 实际产品
public class GoodHtmlDocument implements HtmlDocument {
  private String md;

  public GoodHtmlDocument(String md) {
    this.md = md;
  }

  @Override
  public String toHtml() {
    System.out.println(md);
    return null;
  }

  @Override
  public void save(Path path) {
  }

}
// 实际产品
public class GoodWordDocument implements WordDocument {
  private String md;

  public GoodWordDocument(String md) {
    this.md = md;
  }

  @Override
  public void save(Path path) {
    System.out.println(md);
  }

}

生成器

将一个复杂对象的构建与表示分离, 使得同样的构建过程可以创建不同表示

public class HtmlBuilder {
  // 分成多个builder, 一步步构建
  private HeadingBuilder headingBuilder = new HeadingBuilder();
  private HrBuilder hrBuilder = new HrBuilder();
  private ParagraphBuilder paragraphBuilder = new ParagraphBuilder();
  private QuoteBuilder quoteBuilder = new QuoteBuilder();

  public String toHtml(String md) {
    StringBuilder buffer = new StringBuilder();
    md.lines().forEach(line -> {
      if (line.startsWith("#")) {
        buffer.append(headingBuilder.buildHeading(line))
          .append("\n");
      } else if (line.startsWith(">")) {
        buffer.append(quoteBuilder.buildQuote(line))
          .append("\n");
      } else if (line.startsWith("---")) {
        buffer.append(hrBuilder.buildHr(line))
         .append("\n");
      } else {
        buffer.append(paragraphBuilder.buildParagraph(line))
          .append("\n");
      }
    });
    return buffer.toString();
  }
}
// 分步骤构建
public class URLBuilderImpl implements URLBuilder {

  private String domain = "www.baidu.com";
  private String scheme = "http";
  private String path = null;
  private Map<String, String> query;

  @Override
  public URLBuilder setDomain(String domain) {
    this.domain = Objects.requireNonNull(domain);
    return this;
  }

  @Override
  public URLBuilder setScheme(String scheme) {
    this.scheme = Objects.requireNonNull(scheme);
    return this;
  }

  @Override
  public URLBuilder setPath(String path) {
    this.path = Objects.requireNonNull(path);
    return this;
  }

  @Override
  public URLBuilder setQuery(Map<String, String> query) {
    this.query = query;
    return this;
  }

  @Override
  public String build() {
    StringBuilder sb =  new StringBuilder();
    sb.append(scheme)
      .append("://")
      .append(domain)
      .append(path);
    if (this.query.size() > 0) {
      sb.append("?");
      this.query.forEach((k, v) -> {
        sb.append(k).append("=").append(v).append("&");
      });
      sb.setLength(sb.length() - 1);
    }
    return sb.toString();
  }

  public URLBuilderImpl() {
  }
}

原型

  • 用原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象

原型模式 就是 Prototype, 只指创建新对象的时候, 根据现有的一个原型来创建.

public class Student  {
  private int id;
  private String name;
  private int score;

  // 复制对象并, 直接返回相同类型, 省去类型转换
  public Student copy() {
    Student std = new Student();
    std.id = this.id;
    std.name = this.name;
    std.score = this.score;
    return std;
  }
}

单例

  • 保证一个类仅有一个实例, 并提供一个访问它的全局访问点

单例模式的目的是为了保证在一个进程中, 某个类有且仅有一个实例.

public class Singleton {
  // 静态字段引用唯一实例:
  private static final Singleton INSTANCE = new Singleton();

  // 通过静态方法返回实例
  public static Singleton getInstance() {
    return INSTANCE;
  }

  // private构造方法, 保证外部不能实例化
  private Singleton() {
  }
}
public class Singleton {
  // 静态字段引用唯一实例:
  public static final Singleton INSTANCE = new Singleton();

  // private构造方法, 保证外部不能实例化
  private Singleton() {
  }
}
  • 对构造函数进行private私有化保护

  • 延迟加载的方式, 多线程的方式会导致错误

  • 使用枚举的方式实现单例

    • 可以避免序列化和反序列化会绕过普通类的private方法
    • 也可以定自己的字段方法等
// java保证枚举类的每个枚举, 都是单例
public enum World {
  // 唯一枚举
  INSTANCE;

  private String name = "world";

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}
  • 通过约定, 在框架中直接使用单例. 例如@Component
posted @ 2020-12-03 08:13  张润昊  阅读(107)  评论(0编辑  收藏  举报