当前位置:   article > 正文

java设计模式学习_java 设计模式 学习

java 设计模式 学习

一、设计模式常用类型

  •  创建型模式:对象的创建与使用分离,不需要关注对象的创建细节,这样可以降低系统的耦合度
  • 结构型模式:或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象;由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性
  • 行为模式:描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配;行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性

Spring常用的设计模式:

  • 工厂模式:只对结果负责,封装创建过程
    • BeanFactory(简单工厂模式)
  • 单例模式:保证独一无二
    • ApplicationContext,@Component
  • 原型模式:可以复制千千万万
    • PrototypeBean
  • 代理模式:找人办事,增强职责
    • ProxyFactoryBean、JdkDynamicAopProxy、CglibAopProxy
  • 委派模式:干活算你,功劳算我
    • DispatcherServlet、BeanDefinitionParserDelegate
  • 策略模式:用户选择,结果统一
    • InstaniationStragegy
  • 模版模式:流程标准化,自己实现定制
    • JdbcTemplate、RestTemplate
  • 适配器模式:兼容转换头
    • AdvisorAdapter、HandlerAdapter
  • 装饰者模式:包装,同宗同源
    • InputStreamSource、EncodedResource、HttpHeadResponseDecorator
  • 观察者模式:在任务完成时通知
    • ContextLoaderListener

二、工厂模式

  • 简单工厂模式
    • 优点:面向对象编程思想,屏蔽代码实现,工厂面向所有调用者
    • 缺点:新增一个产品的话会对代码进行重写,不符合OCP开闭原则

           

  • 工厂方法模式
    • 优点:满足里氏替换原则、迪米特法则、开闭原则;灵活型强,新产品只要新增一个工厂实现类就好
    • 缺点:工厂实现类的个数随着时间推移会增多,增加复杂度;只能生产一种产品(游戏类),此弊端可以用抽象工厂解决

                

  •  抽象工厂模式
    • 优点:一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建);
    • 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要修改工厂抽象类里加代码,又修改具体的实现类里面加代码;增加了系统的抽象性和理解难度
    • 适用于:定义好产品族系列产品不变,产品级别可变的情况

     

 三、单例模式

        单例模式需要构造方法私有化,提供公共静态方法获取单例对象

  • 饿汉模式
    • 对象初始化时创建静态私有的单一对象,初期就建好
  • 赖汉模式
    • 需要的时候再创建,原则上只new一次对象,多线程可能出现线程安全问题;可以通过同步法则规避此问题
  • 静态内部类构造对象,内部类中new一个需要的对象,外部类暴露调用接口(延迟加载,线程安全)
  • 枚举类型,保证实例唯一和线程安全

四、原型模式

        原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,类似可以参考java中浅拷贝和深拷贝(一般都是浅拷贝)

  • 浅拷贝,拷贝的新对象中包含原有对象的基本类型和对象引用类型;特别注意Spring中BeanUtils.copyProperties()只会拷贝可读可写的属性(含有set方法,反射机制读(get)然后重写(set)到新对象中),Cloneable接口clone()默认也是实现的浅拷贝(引用类型需要自己来实现new)
  • 深拷贝,整个对象包括内容全部重新创建,最佳方式是利用序列化重新创建(实现Serializable);
    • 利用ObjectMapper来json格式转化(objectMapper.readValue(objectMapper.writeValueAsString(person), Man.class)),但对象包括所有属性对象要实现Serializable并拥有默认构造和set/get方法(json字符串转对象需要有无参构造和set/get方法)——太麻烦
    • 利用java流来实现(ObjectOutputStream(byteArrayOutputStream).writeObject(object);ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())).readObject())——对象和对象属性只需要实现Serializable,相对简单,并且byteArrayOutputStream不用关闭流,close()方法也是空的,用完gc回收了

五、建造者模式

       建造者(Builder)模式的主要角色如下:

  1. 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
  2. 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
  3. 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
  4. 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

        

         和工厂模式的区别:

  • 建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。
  • 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
  • 关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。
  • 建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。
  • 创建复杂对象,就可以考虑使用建造者模式

六、代理模式

  • 静态代理

        由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了,同时目标类需要实现父接口

        

         很明显如果女人改变了,媒人也需要修改信息,违背了OCP开闭原则

  • JDK动态代理

        运行时在内存动态创建代理,利用jdk自带的Proxy工具创建代理,并且实现InvocationHandler接口,而且目标类需要实现父接口或继承父类

        

  •  cglib动态代理

        也是在运行时内存动态创建代理类或实现接口,目标类不用实现或继承类,又叫子类代理,代理类是目标类的子类,比如springAop中的ObjenesisCglibAopProxy代理实现

        

七、适配器模式

        适配器

  • 模式特点
    • 适配器模式(Adapter)包含以下主要角色。
    • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
    • 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
    • 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
  • 类适配:继承适配者使自己成为适配者,不提倡

  •  组合适配:委派式调用,更灵活

八、桥接模式

        桥接(Bridge)模式的定义如下:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

        桥接(Bridge)模式包含以下主要角色:

  1. 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
  2. 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  3. 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
  4. 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

 九、装饰者模式

        装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

        装饰器模式主要包含以下角色:

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

  十、外观模式(门面模式)

         外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。   

        外观(Facade)模式包含以下主要角色:

  1. 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
  2. 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
  3. 客户(Client)角色:通过一个外观角色访问各个子系统的功能。

  十一、亨元模式(后期代码维护优化可以用到)

        享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

        享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。

  • 内部状态指对象共享出来的信息,存储在享元信息内部,并且不会随环境的改变而改变;
  • 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享,可以动态注入。

享元模式的主要角色有如下:

  • 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
  • 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
  • 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
  • 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

备注:常见的-128~127的整型包装类常量池(初始化就创建好)以及字符串常量池都是利用亨元模式,大大节省内存空间,物理上放在堆内存中,逻辑上放在方法区(jdk1.8后是元空间的实现)

 十二、组合模式

        组合(Composite Pattern)模式的定义:有时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构设计模式。

        层层嵌套,类似与链表结构;整体-部分结构中部分数目不宜过多,特别是部分如果差别很大,使结构复杂,不易读难维护;不容易使用继承增加构建新方法。

         组合模式包含以下主要角色。

  1. 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
  2. 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
  3. 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

 十三、观察者模式

        当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

        观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。

代码大致这样......

  • 定义观察目标对象Wallet
  1. package com.demon.design.observer;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * 个人钱包信息,管理订阅者
  6. *
  7. * @since 2022/06/23
  8. */
  9. public class Wallet {
  10. private List<PaymentObject> observers = new ArrayList<>();
  11. private double money;
  12. public void addObserver(PaymentObject observer) {
  13. observers.add(observer);
  14. }
  15. public void removeObserver(PaymentObject observer) {
  16. observers.remove(observer);
  17. }
  18. public void setState(int state) {
  19. // 进账了...
  20. notifyAllObservers();
  21. }
  22. private void notifyAllObservers() {
  23. for (PaymentObject paymentObject : observers) {
  24. paymentObject.phoneTooltip();
  25. }
  26. }
  27. }
  • 定义 观察者抽象类PaymentObject
  1. package com.demon.design.observer;
  2. /**
  3. * 定义被欠款方观察者,观察欠款方钱包,有入账提示要还款。。。。。
  4. *
  5. * @since 2022/06/23
  6. */
  7. public abstract class PaymentObject {
  8. protected Wallet wallet;
  9. public abstract void phoneTooltip();
  10. }
  • 银行扩展了观察者 PaymentObject,设置PaymentBank观察者实体类
  1. package com.demon.design.observer;
  2. public class PaymentBank extends PaymentObject{
  3. public PaymentBank(Wallet wallet) {
  4. this.wallet = wallet;
  5. this.wallet.addObserver(this);
  6. }
  7. @Override
  8. public void phoneTooltip() {
  9. System.out.println("你钱包有一笔钱新入账,可以还钱给工商银行了吧!");
  10. }
  11. }
  •  妻子扩展了观察者 PaymentObject,设置PaymentWife观察者实体类
  1. package com.demon.design.observer;
  2. public class PaymentWife extends PaymentObject{
  3. public PaymentWife(Wallet wallet) {
  4. this.wallet = wallet;
  5. this.wallet.addObserver(this);
  6. }
  7. @Override
  8. public void phoneTooltip() {
  9. System.out.println("你钱包有一笔钱新入账,可以上交给老婆了吧!");
  10. }
  11. }
  •  儿子扩展了观察者 PaymentObject,设置PaymentChildren观察者实体类
  1. package com.demon.design.observer;
  2. public class PaymentChildren extends PaymentObject{
  3. public PaymentChildren(Wallet wallet) {
  4. this.wallet = wallet;
  5. this.wallet.addObserver(this);
  6. }
  7. @Override
  8. public void phoneTooltip() {
  9. System.out.println("你钱包有一笔钱新入账,可以给儿子交学费了吧!");
  10. }
  11. }
  •  建立服务,运行程序......
  1. package com.demon.design.observer;
  2. public class MainRunner {
  3. public static void main(String[] args) {
  4. Wallet wallet = new Wallet();
  5. PaymentObject paymentBank = new PaymentBank(wallet);
  6. PaymentObject paymentWife = new PaymentWife(wallet);
  7. PaymentObject paymentChildren = new PaymentChildren(wallet);
  8. // 钱包入账了...
  9. wallet.setState(100);
  10. // 通知大佬们可以向我要钱了...
  11. }
  12. }

你钱包有一笔钱新入账,可以还钱给工商银行了吧!
你钱包有一笔钱新入账,可以上交给老婆了吧!
你钱包有一笔钱新入账,可以给儿子交学费了吧 !      

 备注:可以参考事件总线框架EventBus,利用观察者模式做为消息队列,只限于单个jvm中(单进程);JdbcTemplate中query方法用到了模板模式和观察者模式,使用的是同步回调方式~~~

十四、策略模式 

        在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式,在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

 策略模式的主要角色如下:

  1. 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  2. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
  3. 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

 十五、命令模式 

        命令(Command)模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理

 命令模式包含以下主要角色:

  1. 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  2. 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  3. 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  4. 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

  十六、迭代器模式 

        迭代器模式(Iterator Pattern)用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。每次都需要新增容器实现类和迭代器实现类

        迭代器模式主要包含以下角色:

  1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

备注:借鉴Collection子集iterator()方法,由于正常集合在遍历过程中改变集合结构(增删),有可能导致不确定性异常,因为每次删除新增都有可能让集合元素重新排布,迭代器模式可以避免这种情况,实际上就是在原有集合上面包装记录有效位置一层,逃离了通过索引位置index的操作。

十七、职责链模式 

      顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

使用场景: 

        1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。

职责链模式主要包含以下角色:

  1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

        备注:常用的两种实现方式,用链表存储处理器和用数组存储处理器,常用的框架有Filter和Interceptor

  • Filter:采用数组存储filter,链路中采用递归调用方法doFilter(...,chain),所以过滤器中有过滤器链chain的说法,实际就是递归实现
  • Interceptor:数据存储拦截器,调用者通过正向循环执行preHandle(...)和逆向循环执行postHandle(...),以及渲染后的afterCompleting(...)调用

 十八、状态模式 

        状态变化影响某一行为,比较鸡肋

 十九、回调模式

       分为同步和异步回调,比模板模式更灵活,JdbcTemplate、RedisTemplate等都有应用

  1. import lombok.Data;
  2. import org.springframework.util.Assert;
  3. import java.io.Serializable;
  4. @Data
  5. public class Person implements Serializable {
  6. private String name;
  7. private int age;
  8. public Person() {
  9. }
  10. public Person(String name, int age) {
  11. this.name = name;
  12. this.age = age;
  13. }
  14. @Override
  15. public String toString() {
  16. return "Person{" +
  17. "name='" + name + '\'' +
  18. ", age=" + age +
  19. '}';
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. public <T> T query(String choose, ResultSetExtractor<T> extract) {
  25. Assert.notNull(choose, "SQL must not be null");
  26. Assert.notNull(extract, "extract must not be null");
  27. class CallBackHand implements ICallBackHand<T> {
  28. public T doInState(String manJson) {
  29. System.out.println(choose + " 调用第三方执行了!");
  30. return extract.extractData("第三方返回数据");
  31. }
  32. }
  33. return this.execute(new CallBackHand());
  34. }
  35. public <T> T execute(ICallBackHand<T> backHand) {
  36. System.out.println("获取第三方的客户工具");
  37. return backHand.doInState("第三方返回结果");
  38. }
  39. public static void main(String[] args) {
  40. Person person = new Person();
  41. person.query("hello", cho -> {
  42. System.out.println("返回需要的执行结果");
  43. return new Man();
  44. });
  45. }
  46. }
  47. interface ICallBackHand<T> {
  48. T doInState(String manJson);
  49. }
  50. @FunctionalInterface
  51. interface ResultSetExtractor<T> {
  52. T extractData(String cho);
  53. }

 二十、Reactor模式(I/O服务模型) 

  • Reactor 的叫法: 1. 反应器模式 2. 分发者模式(Dispatcher) 3. 通知者模式(notifier)

        也叫多路复用路由监听处理

  • 针对channel,java提供了nio包,也叫New IO,是由channel、buffer、selector组成。

       buffer:缓存数据,写入渠道或从渠道读取。

       channel:双向(可读写),可以异步读写,只支持批量(buffer)读写

       selector:监听多个渠道是否做好读写准备,配合非阻塞IO实现单个线程管理多个渠道。

二十一、备忘录模式     

        也叫快照(Snapshot)模式,指在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。主要用在数据防丢失,恢复,撤销等场景,备忘录模式更侧重代码的设计和实现,且备份对象仅仅可读。

        对大数据量处理时,可以针对不同应用场景指定合理的备份措施,如果需要做好原数据备份,可以采用低频率全量备份和高频率增量备份

二十二、解释器模式

        按照语法规则解释,为不同应用场景提供支持

二十三、中介模式

        定义了一个单独的对象,来封装一组对象之间的交互,将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。

        中介模式的设计思想和中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系从多对多(网状关系)转换为一对多(星状关系)。这组对象之间可以是单向交互也可以是双向交互,适应错综复杂的应用场景,通过中介对象来统一调度。一般对象之间交互较为复杂时才考虑,比如飞机航线的中台...

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/213357
推荐阅读
相关标签
  

闽ICP备14008679号