作者备注:该文未全部完成,正在编辑完善中
文字结构:
第1章为类型
第2章为概念
第3章为概念和优缺点
第4章为详细或demo
第1章 Java语言中常用的设计模式有23种,它们被分为三大类:
1、创建型模式(Creational Patterns)
2、结构型模式(Structural Patterns)
3、行为型模式(Behavioral Patterns)
第2章
创建型模式(Creational Patterns):
1、工厂方法模式(Factory Method Pattern)
1.1、简单工厂模式(Simple Factory Pattern)
2、抽象工厂模式(Abstract Factory Pattern)
3、建造者模式(Builder Pattern)
4、原型模式(Prototype Pattern)
5、单例模式(Singleton Pattern)
结构型模式(Structural Patterns):
6、适配器模式(Adapter Pattern)
7、桥接模式(Bridge Pattern)
8、组合模式(Composite Pattern)
9、装饰者模式(Decorator Pattern)
10、外观模式(Facade Pattern)
11、享元模式(Flyweight Pattern)
12、代理模式(Proxy Pattern)
行为型模式(Behavioral Patterns):
13、 责任链模式(Chain of Responsibility Pattern)
14、命令模式(Command Pattern)
15、解释器模式(Interpreter Pattern)
16、迭代器模式(Iterator Pattern)
17、中介者模式(Mediator Pattern)
18、备忘录模式(Memento Pattern)
19、观察者模式(Observer Pattern)
20、状态模式(State Pattern)
21、策略模式(Strategy Pattern)
22、模板方法模式(Template Method Pattern)
23、访问者模式(Visitor Pattern)
第3章
创建型模式(Creational Patterns):
(1.1)、简单工厂模式:这不是一种设计模式,反而比较像是一种编程习惯。
1、工厂方法模式(Factory Method Pattern)
概念:定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延 迟到其工厂的子类。
优点: 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程; 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改, 满足开闭原则;
缺点: 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
2、抽象工厂模式(Abstract Factory Pattern)
概念:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构
优点: 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点: 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
3、建造者模式(Builder Pattern)
4、原型模式(Prototype Pattern)
5、单例模式(Singleton Pattern)
1、工厂方法模式(Factory Method Pattern)
1.1、简单工厂模式(Simple Factory Pattern)
简单工厂模式 是工厂方法设计模式的一种实现方式,用于封装对象的实例化过程。在该模式中,通过一个工厂类来根据客户端的 参数 请求创建不同的产品对象,从而实现对象的创建与使用代码的解耦。但是 简单工厂模式违反了开闭原则
简单工厂模式的一般结构和要点:
-
产品接口(Product):定义了具体产品对象的共同方法,可以是一个抽象类或者接口。
-
具体产品(Concrete Product):实现了产品接口的具体产品类。
-
工厂类(Factory):负责创建具体产品对象的类。它通常包含一个静态方法,根据客户端的请求来创建相应的产品对象。
// 产品接口
interface Product {
void operation();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("生产了A产品");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("生产了B产品");
}
}
// 工厂类
class Factory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
} else {
return null;
}
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
// 使用工厂类创建产品A
Product productA = Factory.createProduct("A");
productA.operation();
// 使用工厂类创建产品B
Product productB = Factory.createProduct("B");
productB.operation();
}
}
小结:
简单工厂模式,客户端通过工厂类的静态方法来创建具体产品的实例,而不需要直接实例化产品对象。客户端只需要知道产品的类型(例如"A"或"B"),工厂类根据类型创建相应的产品对象。这样可以隐藏实例化过程,使得客户端与具体产品的实现解耦。
简单工厂模式适用于以下场景:
- 客户端只需要知道产品的类型,不需要关心具体产品的实现细节。
- 需要集中管理对象的创建过程,避免在多个地方重复创建对象的代码。
- 需要通过一个公共接口来创建不同类型的产品对象。
注意:
简单工厂模式违反了开闭原则,如果需要添加新的产品类型,就需要修改工厂类的代码。如果需要更好的扩展性和灵活性,可以考虑使用工厂方法模式或抽象工厂模式。
1.2、工厂方法模式(Factory Method Pattern)
用于创建对象的实例化过程。在该模式中,定义一个创建对象的接口,但将实际创建对象的过程延迟到子类中进行。这样,可以将对象的实例化与使用代码解耦,使得代码更具有灵活性和可扩展性。
以下是工厂方法模式的一般结构和要点:
-
产品接口(Product):定义了具体产品对象的共同方法,可以是一个抽象类或者接口。
-
具体产品(Concrete Product):实现了产品接口的具体产品类。
-
工厂接口(Creator):声明了创建产品对象的工厂方法,可以是一个抽象类或者接口。这个方法返回一个产品对象。
-
具体工厂(Concrete Creator):实现了工厂接口,负责创建具体产品对象。
// 产品接口
interface Product {
void operation();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("生产了A产品");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("生产了B产品");
}
}
// 工厂接口
interface Factory {
Product createProduct();
}
// 具体工厂A
class FactoryA implements Factory {
@Override
public Product createProduct() {
System.out.print("由A工厂");
return new ConcreteProductA();
}
}
// 具体工厂B
class FactoryB implements Factory {
@Override
public Product createProduct() {
System.out.print("由B工厂");
return new ConcreteProductB();
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
// 创建具体工厂A
Factory factoryA = new FactoryA();
// 使用具体工厂A创建产品
Product productA = factoryA.createProduct();
productA.operation();
// 创建具体工厂B
Factory factoryB = new FactoryB();
// 使用具体工厂B创建产品
Product productB = factoryB.createProduct();
productB.operation();
}
}
小结:
工厂方法模式通过引入抽象工厂类,将具体产品的创建交给子类实现,实现了对产品创建过程的解耦。当需要新增产品时,只需要新增具体产品类和对应的具体工厂类,不需要修改已有代码,符合开闭原则。
工厂方法模式可以应对不同类型的产品创建需求,每个具体工厂类负责创建一类产品,从而提供了更大的灵活性和扩展性。
2、Java中的抽象工厂模式
用于创建一组相关或相互依赖的对象。抽象工厂模式提供一个接口来创建一系列具有共同主题的产品,而不需要指定具体的产品类。
以下是抽象工厂模式的一般结构和要点:
-
抽象产品接口(Abstract Product):定义了具体产品对象的共同方法,可以是一个抽象类或接口。
-
具体产品(Concrete Product):实现了抽象产品接口的具体产品类。
-
抽象工厂接口(Abstract Factory):定义了创建一组相关产品对象的方法。
-
具体工厂(Concrete Factory):实现了抽象工厂接口,负责创建一组相关产品对象
// 抽象产品接口 Cpu
interface Cpu {
void operationCpu();
}
// 抽象产品接口 硬盘
interface Disk {
void operationDisk();
}
//具体产品 华为Cpu
class HuaWeiCpu implements Cpu {
@Override
public void operationCpu() {
System.out.println("华为Cpu");
}
}
//具体产品 小米Cpu
class XiaomiCpu implements Cpu {
@Override
public void operationCpu() {
System.out.println("小米Cpu");
}
}
// 具体产品 华为硬盘
class HuaWeiDisk implements Disk {
@Override
public void operationDisk() {
System.out.println("华为硬盘");
}
}
// 具体产品 小米硬盘
class XiaomiDisk implements Disk {
@Override
public void operationDisk() {
System.out.println("小米硬盘");
}
}
// 抽象工厂接口
interface AbstractFactory {
Cpu operationCpu();
Disk operationDisk();
}
// 具体工厂 华为工厂
class HuaWeiFactory implements AbstractFactory {
@Override
public Cpu operationCpu() {
return new HuaWeiCpu();
}
@Override
public Disk operationDisk() {
return new HuaWeiDisk();
}
}
// 具体工厂 小米工厂
class XiaomiFactory implements AbstractFactory {
@Override
public Cpu operationCpu() {
return new XiaomiCpu();
}
@Override
public Disk operationDisk() {
return new XiaomiDisk();
}
}
//客户端测试
class Test {
public static void main(String[] args) {
// 创建具体工厂 华为
AbstractFactory huaweiFactory = new HuaWeiFactory();
// 使用具体工厂 创建产品cpu和产品硬盘
Cpu huaWeiCpu = huaweiFactory.operationCpu();
Disk huaWeiDisk = huaweiFactory.operationDisk();
huaWeiCpu.operationCpu();
huaWeiDisk.operationDisk();
// 创建具体工厂 小米
AbstractFactory xiaomiFactory = new XiaomiFactory();
// 使用具体工厂 创建产品cpu和产品硬盘
Cpu xiaomiCpu = xiaomiFactory.operationCpu();
Disk xiaomiDisk = xiaomiFactory.operationDisk();
xiaomiCpu.operationCpu();
xiaomiDisk.operationDisk();
}
}
小结:
客户端只需要使用抽象工厂接口和抽象产品接口来创建和使用产品,而无需关心具体产品的实现细节。
抽象工厂模式适用于以下场景:
- 需要创建一组相关或相互依赖的产品对象。
- 客户端不关心具体产品的实现细节,只需要使用产品的接口。
- 需要确保一组产品对象被一起创建,避免不一致或错误的配置。
通过抽象工厂模式,可以实现产品族的切换和扩展,同时也符合开闭原则,因为可以通过添加新的具体工厂类和具体产品类来扩展产品族,而无需修改已有的代码。
3、建造者模式(Builder Pattern)
用于创建复杂对象。建造者模式将对象的构建过程和表示分离,使得同样的构建过程可以创建不同的表示。
以下是建造者模式的一般结构和要点:
-
产品类(Product):表示被构建的复杂对象。产品类通常包含多个属性。
-
抽象建造者(Builder):定义了构建产品的抽象方法,以及设置产品各个部分的方法。
-
具体建造者(Concrete Builder):实现抽象建造者接口,实现具体产品的构建过程。具体建造者通常包含一个具体产品实例作为成员变量,以便在构建过程中逐步组装产品。
-
指挥者(Director):负责使用具体建造者构建产品。指挥者定义了构建产品的顺序和步骤,可以通过指挥者来统一构建过程。
class Student {
private String name;
private Integer age;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
class Builder{
private Student student;
public Builder(){
student = new Student();
}
public Builder setName(String name){
this.student.setName(name);
return this;
}
public Builder setAge(Integer age){
this.student.setAge(age);
return this;
}
public Builder setAddress(String address){
this.student.setAddress(address);
return this;
}
public Student builder(){
return student;
}
}
class Test{
public static void main(String[] args) {
Student student = new Builder()
.setName("张三")
.setAge(18)
.setAddress("地址信息")
.builder();
System.out.println(student);
}
}
4、原型模式(Prototype Pattern)
核心思想是通过克隆(Clone)已有的原型对象来创建新的对象实例。Java中,原型模式的实现依赖于java.lang.Cloneable
接口和clone()
方法。
以下是原型模式的一般结构和要点:
-
原型接口(Prototype):定义了克隆方法
clone()
,用于复制现有对象并创建新对象。 -
具体原型类(Concrete Prototype):实现原型接口,实现
clone()
方法,完成对象的复制。 -
客户端(Client):使用原型对象创建新对象的客户端代码。客户端通过调用原型对象的
clone()
方法来创建新对象,而不是使用构造函数或工厂方法。
class Student implements Cloneable{
private String name;
private Integer age;
private String address;
private List<String> friends;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<String> getFriends() {
return friends;
}
public void setFriends(List<String> friends) {
this.friends = friends;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
", friends=" + friends +
'}';
}
}
class Builder{
private Student student;
public Builder(){
student = new Student();
}
public Builder setName(String name){
this.student.setName(name);
return this;
}
public Builder setAge(Integer age){
this.student.setAge(age);
return this;
}
public Builder setAddress(String address){
this.student.setAddress(address);
return this;
}
public Builder setFriends(List<String> friends) {
this.student.setFriends(friends);
return this;
}
public Student builder(){
return student;
}
}
class Test{
public static void main(String[] args) {
List<String> friends = new ArrayList<>();
friends.add("小明");
friends.add("小wang");
friends.add("小米");
friends.add("小高");
Student student = new Builder()
.setName("张三")
.setAge(18)
.setAddress("地址信息")
.setFriends(friends)
.builder();
System.out.println(student);
Student student2 = (Student) student.clone();
student2.setName("李四");
System.out.println(student2);
System.out.println("============浅拷贝==============");
friends.add("WWWWW");
System.out.println(student);
System.out.println(student2);
System.out.println("是否是同一个:"+(student ==student2));
}
}
运行结果
小结:clone()
方法是浅克隆,即只复制对象本身,而不复制其引用的对象。如果需要实现深克隆,需要在clone()
方法中手动复制引用对象。
原型模式适用于以下场景:
- 创建对象的过程比较复杂,而复制已有对象的成本较低。
- 需要避免使用显式的构造函数来创建新对象。
- 需要动态地创建对象的副本。
通过原型模式,可以避免重复的对象创建过程,提高性能,并且可以动态地创建新对象,使得对象的创建更加灵活和可扩展。
试试深克隆
//省略重复代码。。。。。。
@Override
protected Object clone() {
try {
//深克隆方法
Student student = (Student) super.clone();
List<String> _friends = new ArrayList<>(this.getFriends());
student.setFriends(_friends);
return student;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
运行结果
深克隆&浅克隆 的总结:
原型模式是一种通过克隆已有对象来创建新对象的设计模式。在某些情况下,我们需要进行深克隆而不仅仅是浅克隆,原因如下:
-
避免共享引用对象:如果原型对象和克隆对象之间共享引用对象,那么对其中一个对象的修改会影响到另一个对象。通过深克隆,可以复制引用对象,使得克隆对象拥有独立的引用对象,从而避免共享引用对象的问题。
-
保护数据完整性:如果对象中包含引用对象,并且这些引用对象的数据是不可变的,那么深克隆可以确保原型对象和克隆对象之间的数据完整性。如果使用浅克隆,克隆对象和原型对象会共享相同的引用对象,可能导致克隆对象在修改引用对象数据时影响到原型对象。
-
处理循环引用:当对象之间存在循环引用时,使用浅克隆可能会导致克隆过程无限循环。通过深克隆,可以避免循环引用的问题,每个对象只被克隆一次。
需要注意的是,深克隆可能会导致性能开销较大,特别是在克隆对象的层次结构较深或对象的数据量较大时。在实际应用中,需要根据具体情况权衡使用浅克隆还是深克隆。
总而言之,通过深克隆可以确保克隆对象与原型对象之间的数据独立性,避免共享引用对象的问题,保护数据完整性,并解决循环引用的情况。这样可以更好地满足原型模式的目标,即通过复制已有对象来创建新对象,而不会对原有对象产生影响。
5、单例模式(Singleton Pattern)
用于确保一个类在全局中有且只有一个实例,并提供全局访问点来访问该实例。
单例模式有以下几种常见的实现方式:
- 懒汉式,线程不安全
- 懒汉式,线程安全
- 饿汉式
- 双重检查锁定
- 静态内部类
1、懒汉式,线程不安全:
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2、懒汉式,线程安全
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3、饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
4、双重检查锁定
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
5、静态内部类
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
小结:双重检查锁定 为什需要 使用 volatile ?
在双重检查锁定的单例模式中,为了确保多线程环境下的线程安全性,需要对共享的实例变量添加volatile
关键字。volatile
关键字的作用是禁止指令重排序和线程的本地缓存,保证多线程环境下的可见性和一致性。
在没有volatile
修饰的情况下,可能会发生以下情况:
-
线程A执行到第一次检查时,发现实例变量为null,然后线程B获取了锁并创建了实例,然后线程A获得锁进入同步块,在这里再次创建实例。
-
当线程A退出同步块并返回实例时,由于指令重排序,可能会先执行写入实例变量的操作,然后再执行实例初始化的操作。这样,线程B在获取到实例时,可能会得到一个未完全初始化的实例。
通过使用volatile
关键字,可以解决上述问题。volatile
关键字保证了在一个线程中对共享变量的修改对其他线程可见,并且禁止了指令重排序。因此,当一个线程修改了实例变量并将其写回主存时,其他线程能够立即看到这个更新的值。
修饰实例变量的目的是为了确保多线程环境下的可见性和一致性,保证线程安全地获取单例实例。
需要注意的是,使用volatile
关键字并不能解决所有线程安全性问题。在双重检查锁定的单例模式中,volatile
关键字仅仅保证了实例变量的可见性和一致性,但并不能保证实例本身的线程安全性。因此,在单例模式的实现中,还需要考虑其他线程安全性的问题,例如对初始化过程的同步处理等。
未完,待补充。。。。。