设计模式
设计模式分类
-
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活
-
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
-
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。这些设计模式特别关注对象之间的通信。
参看链接:https://blog.csdn.net/gonghaiyu/article/details/108429391
速记:5、7、11、23都是奇数
- 5种创建型
- 7种结构型
- 11种行为型
创建型:抽工单建原型
- 抽象工厂、工厂、单例、建造者、原型
结构型:桥代理装饰适配器,享元组合成门面
- 桥接、代理、装饰器、适配器、享元、组合、门面(外观)
行为型:观察模板迭代的状态,命令中介解释职责链,访问策略备忘录
- 观察者、模板、迭代、状态、命令、中介者、解释器、职责链、访问者、策略、备忘录
单例模式
系统中类的实例只有一个。
JDK public static Runtime getRuntime()
懒汉式
public class Singleton {
private static Singleton singleton;
private Singleton() {}
// 两个线程同时调用时,可能会返回两个不同实例。改造方法是,加synchronized
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
双重加锁
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
在上面的示例中,关键点如下:
- 使用 volatile 关键字修饰 instance 变量。volatile 保证了变量的可见性,确保当一个线程修改了 instance 的值时,其他线程能够立即看到。
- 在 getInstance() 方法中,首先进行了一个快速的非同步检查,以避免多个线程进入同步块。这是第一次检查。
- 如果第一次检查后,发现 instance 仍然为 null,则进入同步块,并再次检查 instance 是否为 null。这是第二次检查,如果 instance 为 null,则创建实例。
这种双重加锁的方式可以有效地保证线程安全,同时也能够避免在每次调用 getInstance() 时都进行同步,提高了性能。但需要注意,双重加锁需要确保 instance 变量使用 volatile 关键字修饰,以确保在多线程环境下的正确性。
饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
建造者模式
- 创建时有很多必填参数需要验证。创建时参数求值有先后顺序、相互依赖。创建有很多步骤,全部成功才能创建对象。
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时
jdk 中 StringBuilder
变种建造者模式
/**
* BuilderMode
*
* @description TODO
* @author 高源
* @date 2022/5/6 14:12
* @version v1.0.0
*/
public class BuilderMode {
private String RequsetMethod;
private String RequsetUrl;
private String ContentType;
private HttpHeaders header;
public BuilderMode(Builder builder) {
this.RequsetMethod =builder.RequsetMethod;
this.RequsetUrl = builder.RequsetUrl;
this.ContentType = builder.ContentType;
this.header = builder.header;
}
// 定义生成器
public static class Builder{
private String RequsetMethod;
private String RequsetUrl;
private String ContentType;
private HttpHeaders header;
public Builder setRequsetMethod(String RequsetMethod){
this.RequsetMethod = RequsetMethod;
return this;
}
public Builder setRequsetUrl(String RequsetUrl){
this.RequsetUrl = RequsetUrl;
return this;
}
public Builder setContentType(String ContentType){
this.ContentType = ContentType;
return this;
}
public Builder setheader(HttpHeaders header){
this.header = header;
return this;
}
public BuilderMode build(){
return new BuilderMode(this);
}
}
}
// 使用
BuilderMode mode = new BuilderMode.Builder().setContentType("json").setRequsetMethod("https://123468").build();
Builder模式的主要效果:
1 ) Builder对象提供给导向器一个构造产品的抽象接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如何装配的。因为产品是通过抽象接口构造的,你在改变该产品的内部表示时所要做的只是定义一个新的生成器。
- 它将构造代码和表示代码分开,Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性,可对构造过程进行更精细的控制,在导向者的控制下一步一步构造产品的。仅当该产品完成时导向者才从生成器中取回它。因此Builder接口相比其他创建型模式能更好的反映产品的构造过程。这使你可以更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。
优点:建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成。
一般来说,如果产品的建造很复杂,那么请用工厂模式;如果产品的建造更复杂,那么请用建造者模式
原型模式
原型模式是基于已有的对象克隆数据,而不是修改原型链!
创建对象的代价太大,而同类的不同实例对象属性值基本一致。通过原型克隆的方式节约资源。
不可变对象通过浅克隆实现。
可变对象通过深克隆实现,深克隆占用资源多。
同一对象不同时间版本,可以对比没变化的浅克隆,变化的深克隆,然后新版本替换旧版本。
public class PrototypeMode implements Cloneable{
// 浅克隆
@Override
public PrototypeMode clone(){
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆对象失败");
}
return (PrototypeMode)object;
}
}
class PrototypeMode2 implements Serializable {
// 深克隆 两种方法
//1、所有的非基本类型都实现Cloneable 接口,与浅克隆一样
//2、让每个类都实现Serializable接口 通过序列化和反序列化操作实现深克隆
public static void main(String[] args) {
PrototypeMode2 prototypeMode = new PrototypeMode2();
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos= null;
PrototypeMode2 clone = null;
try {
oos = new ObjectOutputStream(bos);
oos.writeObject(prototypeMode);
oos.flush();
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
clone = (PrototypeMode2) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
System.out.println("序列化克隆失败");
}finally {
try {
oos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
简单工厂模式
一个抽象接口,多个抽象接口的实现类,一个工厂类
例子:JSONArray.fromObject(object)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnoORrTF-1684813123048)(null)]
// 抽象产品类
abstract class Car {
public void run();
public void stop();
}
// 具体实现类
class Benz implements Car {
public void run() {
System.out.println("Benz开始启动了。。。。。");
}
public void stop() {
System.out.println("Benz停车了。。。。。");
}
}
class Ford implements Car {
public void run() {
System.out.println("Ford开始启动了。。。");
}
public void stop() {
System.out.println("Ford停车了。。。。");
}
}
// 工厂类
class Factory {
public static Car getCarInstance(String type) {
Car c = null;
if ("Benz".equals(type)) {
c = new Benz();
}
if ("Ford".equals(type)) {
c = new Ford();
}
return c;
}
}
public class Test {
public static void main(String[] args) {
Car c = Factory.getCarInstance("Benz");
if (c != null) {
c.run();
c.stop();
} else {
System.out.println("造不了这种汽车。。。");
}
}
}
工厂方法模式
有四个角色,抽象工厂模式,具体工厂模式,抽象产品模式,具体产品模式。不再是由一个工厂类去实例化具体的产品,而是由抽象工厂的子类去实例化产品
// 抽象产品角色
public interface Moveable {
void run();
}
// 具体产品角色
public class Plane implements Moveable {
@Override
public void run() {
System.out.println("plane....");
}
}
public class Broom implements Moveable {
@Override
public void run() {
System.out.println("broom.....");
}
}
// 抽象工厂
public abstract class VehicleFactory {
abstract Moveable create();
}
// 具体工厂
public class PlaneFactory extends VehicleFactory {
public Moveable create() {
return new Plane();
}
}
public class BroomFactory extends VehicleFactory {
public Moveable create() {
return new Broom();
}
}
// 测试类
public class Test {
public static void main(String[] args) {
VehicleFactory factory = new BroomFactory();
Moveable m = factory.create();
m.run();
}
}
抽象工厂模式
与工厂方法模式不同的是,工厂方法模式中的工厂只生产单一的产品,而抽象工厂模式中的工厂生产多个产品
/抽象工厂类
public abstract class AbstractFactory {
public abstract Vehicle createVehicle();
public abstract Weapon createWeapon();
public abstract Food createFood();
}
//具体工厂类,其中Food,Vehicle,Weapon是抽象类,
public class DefaultFactory extends AbstractFactory{
@Override
public Food createFood() {
return new Apple();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
@Override
public Weapon createWeapon() {
return new AK47();
}
}
//测试类
public class Test {
public static void main(String[] args) {
AbstractFactory f = new DefaultFactory();
Vehicle v = f.createVehicle();
v.run();
Weapon w = f.createWeapon();
w.shoot();
Food a = f.createFood();
a.printName();
}
}
桥接模式
让接口成为实现类的桥梁。
https://www.runoob.com/design-pattern/bridge-pattern.html
过滤器模式
过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。
java8的stream 的filiter() 方法。
https://www.runoob.com/design-pattern/filter-pattern.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TYaIfNEq-1684813122941)(null)]
组合模式
比如所有的网站使用者都叫用户,虽然用户中有管理员,游客,注册者。但是都是用户。
public class Employee {
private int salary;
private List<Employee> subordinates;
//构造函数
public Employee(String name,String dept, int sal) {
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
}
// 组合模式
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO", 30000);
Employee headSales = new Employee("Robert","Head Sales", 20000);
Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//打印该组织的所有员工
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
}
装饰器模式
举例 JDK的 IO 流。
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destinationPath+getPingYin(newFileName)),"UTF-8"));
说明:
// 父类 一个属性,食物
public class Food {
private String food_name;
public Food() {
}
public Food(String food_name) {
this.food_name = food_name;
}
public String make() {
return food_name;
};
}
// n 个子类
//面包类
public class Bread extends Food {
private Food basic_food;
public Bread(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+面包";
}
}
//奶油类
public class Cream extends Food {
private Food basic_food;
public Cream(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+奶油";
}
}
//蔬菜类
public class Vegetable extends Food {
private Food basic_food;
public Vegetable(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+蔬菜";
}
}
// 实现类 自由组合,想吃什么加什么。
public class Test {
public static void main(String[] args) {
Food food = new Bread(new Vegetable(new Cream(new Food("香肠"))));
System.out.println(food.make());
}
}
// 输出
香肠+奶油+蔬菜+面包
适配器模式
忽略底层的实现,向外提供一个统一的接口。好比,开车。不管车的结构如何。驾驶者只要向左打方向盘,车就向左。
比如要返回给第三方系统用户信息。本身系统对接了两个系统的用户信息。通过适配,返回给第三方系统统一格式的用户数据就可以。
public static List asList(T… a)方法
//目标:发动机
interface User
{
public void Info();
}
//系统a的用户信息
class AUser
{
public void AInfo()
{
System.out.println("A系统用户信息");
}
}
//系统b的用户信息
class BUser
{
public void BInfo()
{
System.out.println("B系统用户信息");
}
}
//A适配器
class AAdapter implements Motor
{
private AAdapter adapter;
public AAdapter()
{
adapter=new AAdapter();
}
public void Info()
{·
emotor.AInfo();
}
}
//B适配器
class BAdapter implements Motor
{
private BAdapter adapter;
public BAdapter()
{
adapter=new BAdapter();
}
public void Info()
{
adapter.BInfo();
}
}
//客户端代码
public class UserAdapterTest
{
public static void main(String[] args)
{
System.out.println("适配器模式测试:");
// 不管调用者是谁
User user=(User) obeject
user.drive();
}
}
享元模式
1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。 2、数据库的数据池。
用 HashMap 存储这些对象。
代理模式
个人理解,相当于方法的加强。例如环绕通知这样的。
JDK中 spring aop。
//代理接口
public interface ProxyInterface {
//需要代理的是结婚这件事,如果还有其他事情需要代理,比如吃饭睡觉上厕所,也可以写
void marry();
//代理吃饭(自己的饭,让别人吃去吧)
//void eat();
//代理拉屎,自己的屎,让别人拉去吧
//void shit();
}
// 代理类
public class WeddingCompany implements ProxyInterface {
private ProxyInterface proxyInterface;
public WeddingCompany(ProxyInterface proxyInterface) {
this.proxyInterface = proxyInterface;
}
@Override
public void marry() {
System.out.println("我们是婚庆公司的");
System.out.println("我们在做结婚前的准备工作");
System.out.println("节目彩排...");
System.out.println("礼物购买...");
System.out.println("工作人员分工...");
System.out.println("可以开始结婚了");
proxyInterface.marry();
System.out.println("结婚完毕,我们需要做后续处理,你们可以回家了,其余的事情我们公司来做");
}
}
// 代理类
public class NormalHome implements ProxyInterface{
@Override
public void marry() {
System.out.println("我们结婚啦~");
}
}
// 测试
public class Test {
public static void main(String[] args) {
ProxyInterface proxyInterface = new WeddingCompany(new NormalHome());
proxyInterface.marry();
}
}
责任链模式
https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html
将一个请求所有可能的响应的对象连成一条链。各自对象处理各自所属的事务。
例如 一次查询请求,从网页上发起。路由服务器要分发给可用的服务器,然后过滤器检查请求的ip合法性等,传给controller ,检查参数等。传给service,查询数据。传给dao.访问缓存或者数据库。
这一条链路,可能有的对象缺少,导致最后无响应。
参考链接:https://juejin.cn/post/7023536216138055716
JDK 中 java.util.logging.Logger
实践:改造对外二维码接口,之前是一个servert中将参数检查,ip校验等所有业务做完。改成责任链模式后,分工明确。极大降低了耦合。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJ5TcItj-1684813122997)(null)]
调用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xm9QYYvB-1684813122370)(C:\Users\cc\AppData\Roaming\Typora\typora-user-images\image-20220507165539456.png)]
命令模式
请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
JDK java.lang.Runnable
解释器模式
这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
JDK java.text.Format 日期处理等函数
迭代器模式
JAVA 中的 iterator
中介者模式
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。
java.util.concurrent.Executor
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIDRzdEu-1684813123104)(null)]
备忘录模式
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
IE 中的后退
数据库的事务管理 rollback
观察者模式
对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
举例,zookeeper leader observer,follower 。 leader 有任何变动,都会发送给observer 和follower.
关键是存放观察者,以便通知观察者。
状态模式
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
将各种具体的状态类抽象出来,成为接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-54ddYdAS-1684813122373)(C:\Users\cc\AppData\Roaming\Typora\typora-user-images\image-20220506171508157.png)]
public interface State {
public void doAction(Context context);
}
public class StartState implements State {
public void doAction(Context context) {
System.out.println("Player is in start state");
context.setState(this);
}
public String toString(){
return "Start State";
}
}
public class StopState implements State {
public void doAction(Context context) {
System.out.println("Player is in stop state");
context.setState(this);
}
public String toString(){
return "Stop State";
}
}
public class Context {
private State state;
public Context(){
state = null;
}
public void setState(State state){
this.state = state;
}
public State getState(){
return state;
}
}
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
StartState startState = new StartState();
startState.doAction(context);
System.out.println(context.getState().toString());
StopState stopState = new StopState();
stopState.doAction(context);
System.out.println(context.getState().toString());
}
}
策略模式
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqGUluFJ-1684813123157)(null)]
线程池的拒绝策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2lUGlpiU-1684813123213)(null)]
项目实践:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CImU5oWV-1684813123394)(null)]
@Service
public class CollectorService {
/**
* 数据收集器
*/
private static final List<DataCollector> collectorsList = new ArrayList<>();
/**
* 获取数据收集器
*/
public synchronized List<DataCollector> getCollectors() {
if (CollectionUtils.isEmpty(collectorsList)) {
collectorsList.clear();
collectorsList.addAll(
SpringApplicationContextUtil.getBeansByType(DataCollector.class)); // 将各类策略收集到map中,也可以是hashMap
}
return collectorsList;
}
}
参考链接:https://juejin.cn/post/7023536216138055716
模板模式
模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。一些方法通用,却在每一个子类都重新写了这一方法。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeQtvvIf-1684813123300)(null)]
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}