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