# Design Patterns **Repository Path**: lidaxier/design-patterns ## Basic Information - **Project Name**: Design Patterns - **Description**: 设计模式学习源码 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-11-25 - **Last Updated**: 2021-06-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Design Patterns ## 面向对象基础 - **对象**是一个自包含的实体,用一组可识别度特性和行为来表示。 - **类**是具有相同属性和功能的对象的抽象的集合。 - 实例,就是一个真实的对象。**实例化**,就是创建对象的过程。 - **构造方法**,其实就是对类进行初始化。 - **方法重载**提供了创建同名的多个方法的能力,但这些方法需使用不同的参数类型。 ### 封装 每个对象都包含它能进行操作所需要的所有信息,这个特性称为**封装**,因此对象不必依赖其他对象来完成自己的操作。这样方法和属性包装在类中,通过类的实例来实现。 好处: 1. 良好的封装能够减少耦合。 2. 类内部的实现可以自由地修改,而不干扰其他代码。 3. 类具有清晰的对外接口。 ### 继承 继承者可以理解为是对**被继承者的特殊化**,因为它除了具备被继承者的特性外,还具备自己独有的特性。 子类继承父类的所有特性,子类不但继承了父类的所有特性,还可以定义新的特性。 如果子类继承于父类。 1. 子类拥有父类**非private**的属性和功能; 2. 子类可以**扩展父类**没有的属性和功能; 3. 子类可以通过**方法重写**实现父类的功能。 #### 优点 - 继承避免了重复。 - 继承可使得修改或扩展继承而来的实现都较为容易。 #### 缺点 - 父类变,子类不得不变。 - 继承会破坏包装,父类实现细节暴露给子类。 - 继承显然是一种类与类之间的强耦合的关系。 #### 合理的利用继承 当两个类之间具备**is-a**的关系时,就可以考虑用继承了,因为这表示一个类是另一个类的特殊种类,而当两个类之间是**has-a**的关系时,表示某个角色具有某一项责任,此时不合适用继承。比如人有两只手,手不能继承人;再比如飞机场有飞机,这飞机也不能去继承飞机场。 ### 多态 多态表示不同的对象可以执行相同的动作,但要通过它们自己的实现代码来执行。(不同的类实现相同的接口) 1. 子类以父类的身份出现。 2. 子类在工作时以自己的方式来实现。 3. 子类以父类的身份出现时,子类特有的属性和方法不可以使用。 ### 抽象类 完全可以考虑把实例化没有任何意义的父类,改成抽象类。 1. 抽象类不能实例化。 (动物实例化是没有意义的) 2. 抽象方法不需被子类重写的方法。 (抽象方法可以看成是没有抽象体的虚方法) 3. 如果类中包含抽象方法,那么类就必须定义为抽象类,无论是否还包含其他一般方法。 **1、我们应该考虑让抽象类拥有尽可能多的共同代码,拥有尽可能少的数据。** **2、抽象类通常代表一个抽象概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所以,在一个以继承关系形成的等级结构里面,树叶节点应当是具体类,而树枝节点均应当是抽象类。** ![1606478065575](.\README.assets\1606478065575.png) ### 接口 interface,接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。一旦类实现了接口,类就可以支持接口所指定的所有属性和成员。声明接口在语法上与声明抽象类完全相同,但不允许提供接口中任何成员的执行方式。所以接口不能实例化,不能有构造方法和字段:不能有修饰符,比如public. private等:不能声明虚拟的或静态的等。还有实现接口的类就必须要实现接口中的所有方法和属性。 一个类可以支持多个接口,多个类也可以支持相同的接口。接口的命名,建议前面加一个大写的字母'I' 孙悟空和机器猫都有超能力,都能变出东西。但"动物"不能变出东西,不能让动物具有变出东西的行为,怎么办?利用接口。 ![1606480764881](README.assets/1606480764881.png) ### 抽象类和接口的区别 #### 从表象来看 1. 抽象类可以给出一些成员的实现,接口却不能包含成员的实现。 2. 抽象类的抽象成员可被子类部分实现,接口的成员需要实现类的完全实现。 3. 一个类只能继承一个抽象类,但可以实现多个接口。 #### 深入看 1. 类是对对象的抽象;抽象类是对类的抽象;接口是行为的抽象。 2. 如果行为跨越不同类的对象,可用接口;对于一些相似的类对象,用继承抽象类。实现接口和继承抽象类是并不冲突的。 3. 从设计的角度讲,抽象类是从子类中发现了公共的东西,泛化出父类,然后子类继承父类;而接口是根本不知子类的存在,方法如何实现还不确认,预先定义。 `抽象类是自底而上抽象出来的,而接口则是自顶而下设计出来的。` ## 单一职责原则 ### 就一个类而言,应该仅有一个引起它变化的原因。 - 无论什么需求要来,都需要改这个类,这样维护麻烦,复用不可能,也缺乏灵活性。 - 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受异性不到的破坏。 - 软件设计真正要做许多内容,就是发现职责并把那些职责相互分离。如果多于一个动机去改变这个类,那么这个类就多于一个职责。 ## 开放封闭原则 ### 开放-封闭原则,是说软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。 - 对扩展开放,对更改封闭。(多扩展,少修改) > 我们尽量应在设计时,考虑到需求的种种变化,把问题想的全了,就不会因为需求一来,手足无措。 > > 开放封闭的原则的意思是说,你设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,**我们增加一些类就完事了**,原来的代码能不动则不动。” > > 面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。这就是开放封闭原则的精神所在。 > > 我们希望的是在开发工作展开不久就知道可能发生的变化。查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难 。 > > 开放-封闭原则是面向对象设计的核心所在。 开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。 **过犹不及。** 那一瞬间,我仿佛感受到了哲学? ## 依赖倒转原则 - 高层模块不应该依赖低层模块,两个都应该依赖抽象。 - 抽象不应该依赖细节,细节应该依赖于抽象。 - 要针对接口编程,不要对实现编程 (修电脑的例子) ## 里氏代换原则 子类型必须能够替换掉它们的父类型 - 只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。 ![1606642332177](README.assets/1606642332177.png) ​ 由于子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就可以扩展。 依赖倒转其实可以说是面向对象设计的标志,用哪种语言编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了 ## 迪米特法则 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 - 在类的结构设计上,每一个类都应当尽量降低成员的访问权限。 - 迪米特法则其根本思想,是强调了类之间的松耦合。 - 类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。 ## 合成/聚合复用原则 尽量使用合成/聚合,尽量不要使用类继承。 ![1607738999050](README.assets/1607738999050.png) **合成/聚合复用原则的好处是**,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。 ![1607751458095](README.assets/1607751458095.png) 继承是一种强耦合结构,父类变,子类必须就变。 **我们在用继承时,一定要在是"is-a'的关系时再考虑使用,而不是任何时候都去使用。**