java设计模式-设计原则-创建型模式-场景理解-第一篇
Posted 小明爱java编程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java设计模式-设计原则-创建型模式-场景理解-第一篇相关的知识,希望对你有一定的参考价值。
一、学习必要性设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
- 可以提高程序员的思维能力,编程能力和设计能力
- 使程序设计更加标准化,代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强
1. 开闭原则
- 对扩展开放,对修改关闭
2. 里氏代换原则
- 任何基类出现的地方,子类一定可以出现
通俗来讲,子类可以实现基类的抽象方法,但不能重写基类的非抽象方法
3. 依赖倒转原则
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
4. 接口隔离原则
- 客戶端不应该被迫依赖于它不使用的方法:一个类对另一个类的依赖应该建立在最小的接口上
5. 迪米特法则
- 最少知识原则
通俗来讲,一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为: LOD。
6. 合成复用原则
- 尽量先使用组合或者聚合等关联关系来实现,其次才使用继承关系来实现
1. 单例设计模式
1.1 介绍:
单例类:只能创建一个实例的类
访问类:使用单例类
单例设计模式有两种:
1.1.1 饿汉式:类加载就会导致该单实例对象被创建
- 静态变量方式
- 静态代码块方式
- 枚举方式
枚举方式实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会加载一次,设计者充分利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中的唯一一种不会被破坏的单例实现模式。
1.1.2 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象才会创建
- 线程不安全
- 线程安全(synchronized)
- 双重检查锁
- 静态内部类方式
在多线程情况下,可能会出现空指针问题,出现的原因是JVM在实例化对象的时候会进行优化和指令重排序操作,要解决双重检查锁模式带来的空指针异常问题,只需要使用volatile关键字,volatile关键字可以保证可见性和有序性
1.2 序列化和反序列化、反射会破坏单例模式
1.2.1 解决序列化和反序列化破坏单例模式
对单例模式的类中添加新的一个方法readResolve,该方法在反序列化时直接放回,从而不放回新的对象,达到单例的效果
package com.itheima.pattern.singleton.demo7;
import java.io.Serializable;
/**
* @version v1.0
* @ClassName: Singleton
* @Description: 静态内部类方式
* @Author: fyp
* @data: 2021年 09月 05日 16:40
*/
public class Singleton implements Serializable {
private Singleton(){
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
//当进行反序列化时,会自动调用该方法,将该方法的返回值直接放回
public Object readResolve(){
return SingletonHolder.INSTANCE;
}
}
1.2.2 解决反射破坏单例模式
对单例模式的构造方法设置只进行一次构造,需要初始化一个flag,通过flag的值来判断是否第一次
package com.itheima.pattern.singleton.demo8;
import java.io.Serializable;
/**
* @version v1.0
* @ClassName: Singleton
* @Description: 静态内部类方式
* @Author: fyp
* @data: 2021年 09月 05日 16:40
*/
public class Singleton implements Serializable {
private static boolean flag = false;
private Singleton(){
synchronized (Singleton.class){
//判断flag的值是否是true,如果是true,说明非第一次访问,直接抛一个异常,如果是false,说明是第一次访问,
if(flag){
throw new RuntimeException("不能创建多个对象");
}
flag = true;
}
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
2. 工厂模式
2.1 介绍:
2.1.1 简单工厂模式
简单工厂不是一种设计模式,反而比较像一种编程习惯
简单工厂模式包含如下角色:
抽象产品:定义了产品的规范,描述了产品的主要特征和功能
具体产品:实现或者继承抽象产品的子类
具体工厂:提供了创建产品的方法,使用者通过该方法来创建产品
类图:
优缺点:
优点:封装了创建对象的过程,可以通过参数直接获取对象,把对象的创建和业务逻辑分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在源代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展
缺点:增加新产品时还是需要修改工厂的代码,违背了“开闭原则”
扩展:
静态工厂
在开发中也有一部分工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它也不是23中设计模式中的。
2.1.2 工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个产品对象,工厂方法使一个产品类的实例化延迟到其工厂的子类
结构:
工厂方法模式的主要角色:
抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
具体工厂:提供了创建产品的方法,使用者通过该方法来创建产品
抽象产品:定义了产品的规范,描述了产品的主要特征和功能
具体产品:实现或者继承抽象产品的子类
类图:
2.1.3 抽象工厂模式
是一种为访问类提供一个创建一组相关或者相关依赖对象的接口,且访问类无需指定所要产品的具体类就能得到同族的不同等级产品的模式结构
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品
结构:
抽象工厂:提供了创建产品的接口,它包含了多个创建产品的方法,可以创建多个不同等级的产品
具体工厂:主要实现抽象工厂中的多个抽象方法,完成具体产品的创建
抽象产品:定义了产品的规范,描述了产品的主要特征和功能,抽象工厂模式有多个抽象产品
具体产品:实现了抽象产品角色锁定义的接口,有具体工厂创建,他同具体工厂之间是多对一的关系
类图:
优缺点:
优点:
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
2.2 模式扩展
简单工厂+配置文件解除解耦
类图:
配置文件:bean.properteis
american=com.itheima.pattern.factory.config_factory.AmericanCoffee
latte=com.itheima.pattern.factory.config_factory.LatteCoffee
新建工厂类自动加载配置文件
public class CoffeeFactory {
//1. 定义容器对象存储咖啡对象
private static HashMap<String,Coffee> map = new HashMap<>();
//2. 加载配置文件,只需加载一次
static{
//2.1 创建Properties对象
Properties p = new Properties();
//2.2 调用p对象中的load方法进行配置文件的加载
InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
p.load(is);
//从p集合中获取全类名并创建对象
Set<Object> keys = p.keySet();
for(Object key : keys){
String className = p.getProperty((String) key);
//调用反射技术创建对象
Class clazz = Class.forName(className);
Coffee coffee = (Coffee) clazz.newInstance();
//将名称和对象存储到容器中
map.put((String) key, coffee);
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
public static Coffee createCoffee(String name){
return map.get(name);
}
}
2.3 应用场景:
1.jdk源码中的Collection类和Interator类,两者之间属于抽象工厂和抽象产品的关系
Collection接口是抽象工厂类,ArrayList是具体的工厂类;
Iterator接口是抽象产品类,ArrayList类中的Iter内部类是具体的产品类,在具体的工厂类中iterator()方法创建具体的产品类的对象
2.DateFormat类中的getInstance()方法使用的是工厂模式
3.Calendar类中的getInstance()方法使用的是工厂模式
3. 原型模式
3.1 介绍
用一个已经创建好的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象
结构:
原型模式包含角色:
抽象原型类:规定了具体原型对象必须实现的clone()方法
具体原型类:实现抽象原型类的clone()方法,它是可被复制额对象
访问类:使用具体原型类中的clone()方法来复制新的对象
原型模式的克隆分为浅克隆和深克隆
类图:
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址
3.2 使用场景:
对象的创建非常复杂,可以使用原型模式快捷创建对象性能和安全要求比较高
深克隆:
因为浅克隆导致对第二个克隆对象改动时导致第一个对象也被改动
解决:
使用序列化和反序列化解决(即深克隆)
4. 建造者模式
4.1 介绍
结构:
建造者模式包含如下角色:
- 抽象建造者类(Builder):这个接口要实现复杂对象的那些部分的创建,并不涉及具体的对象的创建
- 具体建造者类(ConcreteBuilder):实现Builder接口,完成复杂产品的各个部件的具体创建方法,在构造过程完成后,提供产品的实例
- 产品类(Product):要创建的复杂对象
- 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或者按某种顺序创建、
类图:
优缺点:
优点:
- 在建造者模式中,客户端不必指定产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
- 可以更加精细地控制产品的创建过程,将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
- 建造者模式很容易进行扩展,如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险,符合开闭原则
缺点:
建造者模式创建的产品一般具有较多的共同点,其他组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
4.2 使用场景:
建造者模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法相对稳定,所以它通常在以下场合使用
- 创建的对象较复杂,由多个部件构成,各部件面临复杂的变化,但构件间的建造顺序是稳定的
- 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,及产品的构建过程和最终的表示是独立的
4.3 模式扩展:
禁用产品的构造方法,构建一个内部类作为建造者,使用建造者的方法来构建,在客户端即可使用链式编程
重构后的代码使用起来更加方便,某种程度上也可以提高开发效率,从软件设计上,对程序员的要求比较高
以上是关于java设计模式-设计原则-创建型模式-场景理解-第一篇的主要内容,如果未能解决你的问题,请参考以下文章