前言

速成一波java的同时,准备把设计模式大概过一下,不要求多么精通,大概了解下概念即可,所以这里写的就不会特别详细。

正文

设计模式三大类别

  • 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式

  • 结构型模式:外观模式、适配器模式、代理模式、组合模式、装饰模式、桥接模式、享元模式

  • 行为型模式:策略模式、责任链模式、观察者模式、模版方法模式、命令模式、迭代器模式、中介者模式、
    备忘录模式、解释器模式、状态模式、访问者模式

设计模式七大原则

  • 总原则-开闭原则:对扩展开放,对修改封闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码。所以一句话概括就是:使程序的扩展性好,易于维护和升级。
  • 单一职责原则:不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分。
  • 里氏代换原则:任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象 化的具体步骤的规范。里氏代换原则中,子类对父类的方法尽量不要重写和重载,因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
  • 依赖倒转原则:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
  • 接口隔离原则:每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
    • 迪米特法则(最少知道原则):一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
    • 合成复用原则:尽量首先使用合成/聚合的方式,而不是使用继承。即首先考虑作为成员变量来调用另一个类的方法。

工厂模式

简单工厂模式

静态工厂模式,实际使用最多。用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

工厂方法模式

又称为工厂模式,多态工厂模式,虚拟构造器模式,简单工厂模式导致工厂一旦需要生产新产品就需要修改工厂类的方法逻辑,违背了“开放 - 关闭”原则。

步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
步骤3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例

工厂方法模式有一组实现了相同接口的工厂类。工厂方法模式把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类。

https://blog.csdn.net/carson_ho/article/details/52343584

抽象工厂模式

可以获得能够生产产品族的工厂类,而不是单一的一个工厂子类对应一个具体的产品。例子类似更换界面主题时,按钮,文本框,背景色等同属于一个产品族的对象可以被直接替换为另一个产品族,很方便。

原理:https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/abstract_factory.html

例子:https://www.runoob.com/design-pattern/abstract-factory-pattern.html

单例模式

例子https://www.runoob.com/design-pattern/singleton-pattern.html

  • 懒汉式:懒汉式是指应用启动时并不会初始化相应的实例,而是在第一次使用时加载,也就是所谓的延时加载吧。线程安全但是效率不高

  • 饿汉式:饿汉式是指应用启动时就初始化相应的实例。线程安全,效率好,不能延时加载。

  • 双重检测锁式(DCL):原理,线程安全,效率高,可以延时加载,实现有点复杂,利用volatile和synchronized两个关键字,防止了jvm的指令重排的导致的线程冲突。
  • 登记式/静态内部类:利用了classloader机制来实现的,线程安全,效率高,可以延时加载
  • 枚举模式:线程安全,调用效率高,不能延时加载

在多线程环境下的效率:饿汉式>静态内部类>枚举式>双重检查锁>懒汉式

单例方式的选用:

  • 单例对象 占用资源少 不需要延时加载:枚举式好于饿汉式
  • 单例对象 占用资源大 需要延时加载:静态内部类好于懒汉式

建造者模式

https://blog.csdn.net/carson_ho/article/details/54910597

  1. 指挥者(Director)直接和客户(Client)进行需求沟通;
  2. 沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求(Builder);
  3. 将各个部件的建造请求委派到具体的建造者(ConcreteBuilder);
  4. 各个具体建造者负责进行产品部件的构建;
  5. 最终构建成具体产品(Product)。

作用:分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责),从而可以构造出复杂的对
象。这个模式适用于在某个对象的构建过程复杂的情况下使用。由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。

原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能(当new一个对象很复杂时)。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在java中类似为克隆技术,以某个对象为原型,复制出新的对象。

优势:效率高(直接克隆,避免了重新执行构造过程步骤)

克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原
型对象相同。并且克隆出的新对象改变不会影响原型对象。然后再修改克隆对象的值。

原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂
方法提供给调用者。

适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。

模式中的角色:

  • 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
  • 需要适配的类(Adaptee):需要适配的类或适配者类。
  • 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。

代理模式

通过代理,控制对对象的访问。可以详细控制访问某个或某类对象的方法,在调用这个方法前做前置
处理,调用这个方法后做后置处理。(是AOP面向切面编程的核心机制)。

例子类似,买火车票可以不用直接去火车站,可以通过售票点

重要角色:

  • 抽象角色:定义代理角色和真实角色的公共对外方法
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用(歌手)
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以
    附加自己的操作。将统一的流程控制放到代理角色中处理(经纪人)

组合模式

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。

例子:算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作数也可以是操作数、操作符和另一个操作数。

装饰器模式

降低系统的耦合度,可以动态地为一个对象增加新的功能。装饰模式是一种用于代替继承的技术。

类似python的装饰器。

主要角色:

  • Component抽象构件角色:真实对象和装饰对象有相同的接口。这样客户端对象就能够以与真实对象相
    同的方式同装饰对象交互。InputStream、OutputStream、Reader、Writer
  • ConcreteComponent具体构件角色(真实对象):io流中的FileInputStream、FileOutputStream 。
  • Decorator装饰角色:持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发
    给真实的对象 。这样,就能在真实对象调用前后增加新的功能。FilterInputStream、
    FilterOutputStream
  • ConcreteDecorator具体装饰角色:负责给构件对象增加新的责任。BufferedOutputStream、
    BufferedInputStream

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

外观模式

隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性

桥接模式

抽象化与实现化解耦,使得二者可以独立变化。这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。使用相同的抽象类方法但是不同的桥接实现类,这个看例子吧,太抽象了。。。https://www.runoob.com/design-pattern/bridge-pattern.html,大概意思就是我用的是同一个抽象类,但是我根据提供的参数(实现化?)进行不同的桥接来使得实现化不同,关键就是这个桥接怎么写。

享元模式

尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象,减少创建对象的数量,以减少内存占用和提高性能。

责任链模式

将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理
该请求,如果能则处理,如果不能则传递给链上的下一个对象。

例子就是部门审批,从组长,部分主管,经理,总经理这种逐级的,java中的try catch机制也是这样。

迭代器模式

这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。也是一种封装吧类似iterator。

中介者模式

如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系极其复杂,这
些对象成为“同事对象”。我们可以引入一个中介者对象,解耦多个同事对象之间的交互关系,每个对象都持有
中介者对象的引用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系 。

例子:mvc中的c控制器就是一个中介者,m和v都是通过c进行交流。

命令模式

将一个请求封装为一个对象,从而使得我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日
志,以及支持可撤销的操作。

例子:数据库的事务机制,命令的撤销和恢复

策略模式

策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可
以方便的更换算法或者增加新的算法,并且由客户端决定调用哪个算法。

本质就是分离算法,选择实现。

例子:ssl中可以选择加密使用的算法

模板方法模式

处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定,于是将这个节点的代码实现转移
给子类实现(处理步骤父类中定义好,具体实现延迟到子类中定义)

例子:servlet中doGet/doPost方法的调用

观察者模式

主要用于1:N的通知。当一个对象(目标对象)的状态变化时,他需要及时告知一系列对象(观察者对
象),令他们做出响应

例子:聊天室中,一个人发了消息,服务端需要推送给所有的用户。

备忘录模式

保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态

例子:数据库中的回滚

总结

大概看一一遍设计模式,没有太详细研究具体实现,感觉比较重要的可能就是那几个,我觉得用到的时候在弄吧,先有个大概印象先,over,接下准备吧java那些servlet,ssm,spring大致平推简单的过一遍,了解下起码的架构和常用方法,到时候学习到什么程度再视情况而定吧,最近的重点还是放在ucore,raft那块,然后手头上的工作也要继续熟悉,为秋招做好准备吧,加油冲冲冲。