面向对象编程有七个经常被提及的原则。 提倡它的根本原因是为了复用代码,增加可维护性。 设计模式实现这些原则以实现代码重用并提高可维护性。
因为设计模式是基于这些原则的实现,所以有必要理解这些原则。
1.单一职责原则(SRP)
英文全称是,定义是一个类,它的改变应该只有一个原因。 阶级变动的原因是责任。 如果一个类承担了太多的职责,就意味着将这些职责耦合在一起。 一项职责的改变可能会削弱或抑制班级履行其他职责的能力。 这种耦合可能会导致脆弱的设计,当发生变化时可能会遭受意想不到的损坏。 如果想要避免这种现象的发生,就必须尽可能遵守单一责任原则。 这一原则的核心是解耦和增加内聚力。
2. 开闭原则(OCP)
英文全称是Open Close,定义是软件实体(包括类、模块、函数等)应该对扩展开放,对修改关闭。 开闭原则是面向对象设计中最重要的原则之一。 许多其他设计原则都是实现开闭原则的手段。
3. 里氏代换原理(LSP)
英文全称是 ,它是面向对象设计的基本原则之一。 定义是任何基类都可以出现的地方,子类必须出现。 LSP是继承重用的基石。 只有当派生类能够替代基类且不影响软件单元的功能时,才能真正重用基类,并且派生类也可以在基类的基础上添加新的。 行为。 里氏替换原理是对开闭原理的补充。 实现开闭原则的关键步骤是抽象,而基类和子类之间的继承关系就是抽象的具体实现,因此里氏替换原则就是对实现抽象的具体步骤的规范。
4. 依赖倒置原则(DIP)
英文全称是,这个原理是开闭原理的基础。 依赖倒置原则要求调用者和被调用者都依赖抽象,使得两者之间没有直接的关联和联系。 改变时,一者的改变不会影响另一者。 一方发生变化。 依赖倒置强调了抽象的重要性。 接口编程依赖于抽象而不是具体。
5、接口隔离原理(ISP)
英文全称是,这个原则的意思是使用多个隔离的接口比使用单个接口要好。 目的是减少类之间的耦合,方便软件升级和维护。
6. 鲜为人知的原则(迪米特原则)
一个实体应尽可能少地与其他实体交互,使系统功能模块相对独立。 通俗地说,就是不要和陌生人说话,即一个学科对其他学科的了解尽可能少。 迪米特定律的初衷是为了减少类之间的耦合。 由于每个类都最大限度地减少了对其他类的依赖,因此很容易使系统的功能模块在功能上独立,彼此之间没有(或很少)依赖关系。
7. 合成/聚合重用(CARP)
英文全称是Reuse,综合/聚合复用原则也常被称为综合复用原则。 综合/聚合复用原则的潜台词是:我只是用你的方法,我们不一定一样。 继承更加耦合。 例如,如果父类后来添加一个接口或删除一个接口,则子类可能会遭受毁灭性的编译错误。 但是,如果只结合聚合,只引用类方法,就不会有这个巨大的风险,同时也实现了复用。
3.创作者模式(5种)
创建模式意味着这些设计模式提供了一种在创建对象时隐藏创建逻辑的方法,而不是直接使用new运算符实例化对象。这使得程序可以更灵活地确定需要为给定实例创建哪些对象。
1. 单例模式
确保一个类只有一个实例,并自己实例化它,将这个实例提供给整个系统。
单例模式并不难理解。 一个典型的例子是一家公司只能有一名CEO。 主要是保证一个类只有一个实例。 此类提供了返回实例的方法。 该方法中,首先判断系统是否已经存在这个单例。 如果是,则返回它。 如果没有,它就会创建它。 如果创建多个实例消耗太多资源,或者某种类型的对象应该只有一个且只有一个,那么您应该考虑使用单例模式。
单例模式并不难理解。 重要的是掌握几种常用的写法。
饿了么中式:
public class Singleton { // 直接创建对象 public static Singleton instance = new Singleton(); // 私有化构造函数 private Singleton() { } // 返回对象实例 public static Singleton getInstance() { return instance; } }
懒人风格:
//写法一、懒汉式写法 public class Singleton { private static Singleton instance; //构造函数私有 private Singleton (){ } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } //写法二、DCL(Double Check Lock) 双重校验锁 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; } } /写法三、静态内部类单例模式 public class Singleton { private Singleton (){ } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } }
上面第一种懒惰式的写法实现了延迟创建和线程安全。 缺点是每次调用()都要进行同步,效率低下。 第二种DCL方法更为常见。 两次都是空的。 第一次为空可以避免不必要的同步,第二次可以确保创建单个实例。 这种方法比较好,但是在高并发环境下有时会出现问题。 。 第三种方法是最推荐的,线程安全也保证了实例的唯一性。
2.工厂方法模式
定义一个用于创建对象的接口,并让子类决定实例化哪个类。
工厂方法模式分为三种: 普通工厂模式,即建立一个工厂类,创建一些实现相同接口的类的实例。 多工厂方法模式是对普通工厂方法模式的改进。 普通工厂方法模式中,如果传递的字符串错误,则无法正确创建对象,而多工厂方法模式提供了多个工厂方法,分别创建。 目的。 静态工厂方法模式使上述多工厂方法模式中的方法静态化。 不需要创建实例,直接调用即可。
3.抽象工厂模式
工厂方法模式的一个问题是类的创建依赖于工厂类。 也就是说,如果要扩展程序,就必须修改工厂类。 这违反了封闭原则。 因此,从设计的角度来看,存在一定的问题。 ,怎么解决? 只需使用抽象工厂模式并创建多个工厂类即可。 一旦需要添加新的功能,直接添加新的工厂类即可,无需修改之前的代码。
4.建造者模式( )
工厂类模式提供了创建单个类的模式,而构建器模式则集中并管理各种产品以创建复合对象。 所谓复合对象是指具有不同属性的类。 事实上,建造者模式是通过前面的抽象工厂模式和最终的Test结合得到的。
5.原型模式
定义
使用原型实例指定要创建的对象类型,并通过复制这些原型来创建新对象。
介绍
原型模式并不难理解。 主要用在创建实例的时候,因为有时候通过new创建对象的成本可能会太高。 这种情况下,我们可以考虑通过直接克隆实例来快速创建对象。 克隆实例具有与原始实例相同的内部属性。 原型模式需要注意深拷贝和浅拷贝的问题。
4. 结构模式(7种)
结构模式侧重于类和对象的组合。 继承的概念用于组合接口并定义组合对象获取新功能的方式。
1. 适配器设计模式
适配器模式将某个类的接口转换为客户端期望的另一种接口表示形式,目的是消除因接口不匹配而导致的类兼容性问题。主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
public class Source { public void method1() { System.out.println("this is original method!"); } } ------------------------------------------------------------- public interface Targetable { /* 与原类中的方法相同 */ public void method1(); /* 新类的方法 */ public void method2(); } public class Adapter extends Source implements Targetable { @Override public void method2() { System.out.println("this is the targetable method!"); } } public class AdapterTest { public static void main(String[] args) { Targetable target = new Adapter(); target.method1(); target.method2(); } }
基本思想与类适配器模式相同,只是修改了类。 这次不是继承类,而是持有类的实例来解决兼容性问题。
public class Wrapper implements Targetable { private Source source; public Wrapper(Source source) { super(); this.source = source; } @Override public void method2() { System.out.println("this is the targetable method!"); } @Override public void method1() { source.method1(); } } -------------------------------------------------------------- public class AdapterTest { public static void main(String[] args) { Source source = new Source(); Targetable target = new Wrapper(source); target.method1(); target.method2(); } }
接口的适配器是这样的:有时候我们写的一个接口中会有多个抽象方法。 当我们编写接口的实现类时,必须实现接口的所有方法。 有时这显然是浪费的。 这时候我们就可以使用一个抽象类,这个抽象类实现了接口和所有方法,而我们不和原来的接口打交道,只接触抽象类,所以我们写一个类,继承抽象类,并重写我们需要的方法即可。
2.桥接模式
将抽象部分与实现部分分开,以便它们可以独立更改。
在软件系统中,有些类型由于自身逻辑的原因,会在两个或多个维度上发生变化。 那么如何应对这种“多维变化”呢? 这就需要使用桥接模式。 桥接模式需要重点理解抽象部分、实现部分、解耦。 一个典型的例子就是咖啡加糖的问题。 抽象部分有 , 在其下面,实现部分有 Sugar ,并且在抽象类中引用它。 这实际上是一座桥。
3、装修模式
顾名思义,装饰模式就是给一个对象添加一些新的功能,而且是动态的。 它要求装饰对象和被装饰对象实现相同的接口,并且装饰对象持有被装饰对象的实例。
4、组合方式
对象被组合成树形结构来表示“部分-整体”层次结构,允许用户一致地使用单个对象和组合对象。
组合模型比较容易理解。 一个典型的例子是假设A公司有不同的部门,不同部门下有不同的员工。 这样,一个部门下的所有员工就合并为一个部门,所有部门又合并为整个公司。 。
5、外观模式
为了给子系统中的一组接口提供一致的接口,外观模式定义了一个高级接口,使子系统更易于使用。
外观模型的一个典型例子就是去医院看病。 挂号、门诊、定价、取药让患者或患者家属感觉非常复杂。 如果有一个接待员就很方便了,只有接待员才能办理。
6. 享元模式
利用共享技术高效支持大量细粒度对象。
当对象数量较多时,可能会出现内存溢出。 我们抽象出公共部分。 如果有相同的业务请求,直接返回内存中已经存在的对象,避免重新创建。
7.代理模式
为其他对象提供代理来控制对此对象的访问。
代理模式主要解决直接访问对象带来的问题。 比如猪八戒去找高翠兰,结果发现他就是孙悟空。 可以这样理解:高翠兰的外表是抽象出来的,高翠兰本人和孙悟空都实现了这个接口。 猪八戒拜访高翠兰的时候,看不出这是孙悟空,所以他说孙悟空是高翠兰的特工班。
5. 行为模式(11种)
这些设计模式特别关注对象之间的通信。
1. 模板方法模式
运行算法的框架将一些步骤推迟到子类中,允许子类在不改变算法结构的情况下重新定义算法的特定步骤。
模板方法模式的一个典型例子就是 中的异步任务类,它封装了异步任务的执行。 当子类继承它时,只需要在指定的流程中实现特定的操作即可。
2. 命令模式
将请求封装为对象,以便客户端可以参数化不同的请求; 对请求进行排队或记录,并支持可取消的操作
命令模式主要是利用调用者调用接收者执行命令。 这种模式下需要理解三个角色:(1)真正的命令执行对象(2)持有要调用的相关方法的引用。 (3) 请求者持有该方法的引用,并调用该方法来执行特定的命令。
3.迭代器模式
提供一种顺序访问聚合对象的元素而不暴露对象的内部表示的方法。
在Java集合框架中,我们知道,对于指定的集合类,我们可以使用特定的迭代器来遍历集合中的所有元素。 结合这一点,迭代器模式就很容易理解了。
4.观察者模式
定义对象之间一对多的依赖关系。 当对象的状态发生变化时,所有依赖于该对象的对象都会收到通知并自动更新。
观察者模式可以结合 来理解。 当数据发生变化时,关联的适配器会通过()方法通知接口刷新。
5、中介模式
使用中介对象来封装一系列对象交互。 调解器消除了对象显式相互引用的需要,因此它们是松散耦合的,并且可以独立地更改它们的交互。
中介模式的典型例子是,加入世贸组织之前,各国之间存在贸易往来,结构复杂。 大家加入世贸组织之后,各国通过世贸组织相互进行贸易,就标准化了。
6.备忘录模式
捕获对象的内部状态并将该状态保存在对象外部而不破坏封装。 这允许您稍后将对象恢复到其保存的状态。
memo模式的一个典型例子就是git版本管理工具,它可以帮助我们在每次提交后保存项目状态,并在需要时回滚到指定版本。
7. 口译模式
给定一种语言,定义其语法的表示,并定义一个使用该表示来解释该语言中的句子的解释器。
解释器的一个典型示例是编译原理的应用,如果特定类型的问题发生得足够频繁,则可能值得将该问题的各个实例表述为简单语言的句子。 这使得构建一个解释器通过解释这些句子来解决问题成为可能。
8.状态模式
允许对象在其内部状态改变时改变其行为。 该对象似乎已修改其类。
状态模式主要解决对象的行为取决于其状态(属性),并且其相关行为可以根据其状态的变化而改变的问题。 一个典型的例子是,一个人完成一件事,在不同的状态下,结果可能会有所不同。
9.策略模式
定义一系列算法,将它们一一封装,并使其可以互换。 这种模式允许算法独立于使用它的客户端而改变。
从策略模式的定义可以看出,它主要是将算法和客户分开。 一个典型的例子就是排序算法。 我们给定一个数组并输出排序结果,但我们可以在此过程中使用不同的排序算法。 这些算法实际上是策略。
10.责任链模型
给多个对象一个处理请求的机会,从而避免请求的发送者和接收者之间的耦合。 这些对象连接成一条链,请求沿着链传递,直到有一个对象处理它。
责任链模式避免了请求发送者和接收者的耦合,允许多个对象潜在地接收请求,将这些对象连接成一条链,并沿着这条链传递请求,直到有一个对象处理它。
11.访客模式
封装一些对数据结构中每个元素进行操作的操作。 它允许您定义作用于每个元素的新操作,而无需更改其类。
访问者模式是一种将数据操作和数据结构分离的设计模式。 通常在对象结构比较稳定的情况下使用,但经常需要对这个对象结构定义新的操作,或者对一个对象结构中的对象执行很多操作。 不同且不相关的操作,并且需要避免这些操作“污染”这些对象的类,请使用访问者模式将它们封装到类中。