精华内容
下载资源
问答
  • 主要为大家详细介绍了C#面向对象设计的七大原则,未读文章之前大家说一说都有哪七大原则,是不是七大原则都可以全部列出,想要了解的朋友可以参考一下
  • 设计模式之七大原则 面向对象的可复用设计的第一块基石便是所谓的开 - 闭原则 开- 闭总原则 开 - 闭原则讲的是一个软件实体应当对扩展开放对修改关闭 这个原则说的是在设计一个模块的时候应当使这个模块可以再不被...
  • 原文作者Guille认为如何让用户以最快的时间获得想要的数据尤为重要,开发者应注重七大原则,包括:服务器渲染、即时响应用户输入、响应数据/状态变更、增强历史记录处理等。对于Web开发者而言,除了具拥有精湛的开发...
  • 第一章设计模式与七大原则笔记,自己看网课写的,需要的关注拿一下吧,23种设计模式,背后其实是七大设计原则,也就是说,每个设计模式都归属于一个或多个设计原则7大设计原则:a单一职责原则 b里氏替换 c依赖倒置...
  • 面向对象设计的七大原则(包括SOLID原则)原则概述1. 单一职责原则(Single Responsibility Principle)2. 开闭原则(Open Close Principle)3. 里氏替换原则(Liskov Substitution Principle)4. 接口隔离原则...

    原则概述

    1. 单一职责原则(Single Responsibility Principle)

    每一个类应该专注于做一件事情。
    

    2. 开闭原则(Open Close Principle)

    面向扩展开放,面向修改关闭。
    

    3. 里氏替换原则(Liskov Substitution Principle)

    超类存在的地方,子类是可以替换的。
    

    4. 接口隔离原则(Interface Segregation Principle)

    应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。
    

    5. 依赖倒置原则(Dependence Inversion Principle)

    实现尽量依赖抽象,不依赖具体实现。
    

    6. 迪米特法则(Law Of Demeter)

    又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。
    

    7. 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)

    尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。
    

    原则详解

    1. 单一职责原则(Single Responsibility Principle)

    因为:

    可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;提高类的可读性,提高系统的可维护性;变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。

    所以:

    从大局上看Android中的Paint和Canvas等类都遵守单一职责原则,Paint和Canvas各司其职。

    示例:

    新建一个Rectangle类,该类包含两个方法,一个用于把矩形绘制在屏幕上,一个方法用于计算矩形的面积。

    Rectangle类违反了SRP原则。Rectangle类具有两个职责,如果其中一个改变,会影响到两个应用程序的变化。

    一个好的设计是把两个职责分离出来放在两个不同的类中,这样任何一个变化都不会影响到其他的应用程序。

    2. 开闭原则(Open Close Principle)

    因为:

    开放封闭原则主要体现在对扩展开放、对修改封闭,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。软件需求总是变化的,世界上没有一个软件的是不变的,因此对软件设计人员来说,必须在不需要对原有系统进行修改的情况下,实现灵活的系统扩展。

    所以:

    可以通过Template Method模式和Strategy模式进行重构,实现对修改封闭,对扩展开放的设计思路。
    封装变化,是实现开放封闭原则的重要手段,对于经常发生变化的状态,一般将其封装为一个抽象,拒绝滥用抽象,只将经常变化的部分进行抽象。

    示例:

    public boolean sendByEmail(String addr, String title, String content) {
    }public boolean sendBySMS(String addr, String content) {
    }
    // 在其它地方调用上述方法发送信息
    sendByEmail(addr, title, content);
    sendBySMS(addr, content);
    

    如果现在又多了一种发送信息的方式,比如可以通过QQ发送信息,那么不仅需要增加一个方法sendByQQ(),还需要在调用它的地方进行修改,违反了OCP原则,更好的方式是

    抽象出一个Send接口,里面有个send()方法,然后让SendByEmail和SendBySMS去实现它既可。这样即使多了一个通过QQ发送的请求,那么只要再添加一个SendByQQ实现类实现Send接口既可。这样就不需要修改已有的接口定义和已实现类,很好的遵循了OCP原则。

    3. 里氏替换原则(Liskov Substitution Principle)

    因为:

    里氏替换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。里氏替换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

    所以:

    使用里氏替换原则时需要注意,子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。

    从大局看Java的多态就属于这个原则。

    4. 接口隔离原则(Interface Segregation Principle)

    不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。

    客户模块不应该依赖大的接口,应该裁减为小的接口给客户模块使用,以减少依赖性。如Java中一个类实现多个接口,不同的接口给不用的客户模块使用,而不是提供给客户模块一个大的接口。

    示例:

    public interface Animal {
    	public void eat(); // 吃
    	public void sleep(); // 睡
    	public void crawl(); // 爬
    	public void run(); // 跑
    }
    
    public class Snake implements Animal {
    	public void eat() {
    	}
    	public void sleep() {
    	}
    	public void crawl() {
    	}
    	public void run() {
    	}
    }
    
    public class Rabit implements Animal {
    	public void eat() {
    	}
    	public void sleep() {
    	}
    	public void crawl() {
    	}
    	public void run() {
    	}
    }
    

    上面的例子,Snake并没有run的行为而Rabbit并没有crawl的行为,而这里它们却必须实现这样不必要的方法,更好的方法是crawl()和run()单独作为一个接口,这需要根据实际情况进行调整,反正不要把什么功能都放在一个大的接口里,而这些功能并不是每个继承该接口的类都所必须的。

    5. 依赖倒置原则(Dependence Inversion Principle)

    因为:

    具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类;而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口;这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。

    所以:

    采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,减少并行开发引起的风险,提高代码的可读性和可维护性。

    从大局看Java的多态就属于这个原则。

    6. 迪米特法则(Law Of Demeter)

    因为:

    类与类之间的关系越密切,耦合度也就越来越大,只有尽量降低类与类之间的耦合才符合设计模式;对于被依赖的类来说,无论逻辑多复杂都要尽量封装在类的内部;每个对象都会与其他对象有耦合关系,我们称出现成员变量、方法参数、方法返回值中的类为直接的耦合依赖,而出现在局部变量中的类则不是直接耦合依赖,也就是说,不是直接耦合依赖的类最好不要作为局部变量的形式出现在类的内部。

    所以:

    一个对象对另一个对象知道的越少越好,即一个软件实体应当尽可能少的与其他实体发生相互作用,在一个类里能少用多少其他类就少用多少,尤其是局部变量的依赖类,能省略尽量省略。同时如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一方法的话,可以通过第三者转发这个调用。

    从大局来说Android App开发中的多Fragment与依赖的Activity间交互通信遵守了这一法则。

    7. 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)

    因为:

    其实整个设计模式就是在讲如何类与类之间的组合/聚合。在一个新的对象里面通过关联关系(包括组合关系和聚合关系)使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用其已有功能的目的。也就是,要尽量使用类的合成复用,尽量不要使用继承。

    如果为了复用,便使用继承的方式将两个不相干的类联系在一起,违反里氏代换原则,哪是生搬硬套,忽略了继承了缺点。继承复用破坏数据封装性,将基类的实现细节全部暴露给了派生类,基类的内部细节常常对派生类是透明的,白箱复用;虽然简单,但不安全,不能在程序的运行过程中随便改变;基类的实现发生了改变,派生类的实现也不得不改变;从基类继承而来的派生类是静态的,不可能在运行时间内发生改变,因此没有足够的灵活性。

    所以:

    组合/聚合复用原则可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

    参考文章:
    https://www.cnblogs.com/sunflower627/p/4718702.html
    https://www.imooc.com/article/51098#

    展开全文
  • 设计模式七大原则

    千次阅读 2018-08-07 12:22:08
    参考自 : 设计模式之六大原则(转载) - 海 子 - 博客园 前言: 设计模式(Design pattern) 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更...

    参考自 : 设计模式之六大原则(转载) - 海 子 - 博客园

    前言:

    设计模式(Design pattern) 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

    使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
    设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。
    只有精通了设计模式,才敢说真正理解了软件工程。
    可以说,设计模式是每一个架构师所必备的技能之一。想要精通设计模式,必须要先搞清楚设计模式的七大原则。

    image.png

    单一职责原则(Single Responsibility Principle,简称SRP)

    一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。

    单一职责原则告诉我们:
    一个类不能太“累”!在软件系统中,一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小.
    而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作.
    因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。

    单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。

    实例:

    Sunny软件公司开发人员针对某CRM(Customer Relationship Management,客户关系管理)系统中客户信息图形统计模块提出了如图所示初始设计方案:

    image.png

    在图中,CustomerDataChart类中的方法说明如下:getConnection()方法用于连接数据库,findCustomers()用于查询所有的客户信息,createChart()用于创建图表,displayChart()用于显示图表。

    现使用单一职责原则对其进行重构。

    CustomerDataChart类承担了太多的职责,既包含与数据库相关的方法,又包含与图表生成和显示相关的方法。
    如果在其他类中也需要连接数据库或者使用findCustomers()方法查询客户信息,则难以实现代码的重用。
    无论是修改数据库连接方式还是修改图表显示方式都需要修改该类,它不止一个引起它变化的原因,违背了单一职责原则。因此需要对该类进行拆分,使其满足单一职责原则,类CustomerDataChart可拆分为如下三个类:

    1. DBUtil:负责连接数据库,包含数据库连接方法getConnection();
    2. CustomerDAO:负责操作数据库中的Customer表,包含对Customer表的增删改查等方法,如findCustomers();
    3. CustomerDataChart:负责图表的生成和显示,包含方法createChart()和displayChart()。

    image.png

    开闭原则(Open-Closed Principle, OCP)

    一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

    在开闭原则的定义中,软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。

    任何软件都需要面临一个很重要的问题,即它们的需求会随时间的推移而发生变化。当软件系统需要面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。随着软件规模越来越大,软件寿命越来越长,软件维护成本越来越高,设计满足开闭原则的软件系统也变得越来越重要。

    为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在Java、C#等编程语言中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中完成。在很多面向对象编程语言中都提供了接口、抽象类等机制,可以通过它们定义系统的抽象层,再通过具体类来进行扩展。如果需要修改系统的行为,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。

    实例:

    image.png

    里氏代换原则(Liskov Substitution Principle, LSP)

    所有引用基类(父类)的地方必须能透明地使用其子类的对象。

    里氏代换原则是实现开闭原则的重要方式之一。在本实例中,在传递参数时使用基类对象,除此以外,在定义成员变量、定义局部变量、确定方法返回类型时都可使用里氏代换原则。针对基类编程,在程序运行时再确定具体子类。

    依赖倒转原则(Dependency Inversion Principle, DIP)

    抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。

    依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。

    在引入抽象层后,系统将具有很好的灵活性,在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需要对抽象层进行扩展,并修改配置文件,而无须修改原有系统的源代码,在不修改的情况下来扩展系统的功能,满足开闭原则的要求。

    在实现依赖倒转原则时,我们需要针对抽象层编程,而将具体类的对象通过依赖注入(DependencyInjection, DI)的方式注入到其他对象中,依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。

    常用的注入方式有三种,分别是:构造注入设值注入(Setter注入)接口注入

    构造注入是指通过构造函数来传入具体类的对象.

    设值注入是指通过Setter方法来传入具体类的对象.

    接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象。

    image.png

    接口隔离原则(Interface Segregation Principle, ISP)

    使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

    根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干。这里的“接口”往往有两种不同的含义:一种是指一个类型所具有的方法特征的集合,仅仅是一种逻辑上的抽象;另外一种是指某种语言具体的“接口”定义,有严格的定义和结构,比如Java语言中的interface。对于这两种不同的含义,ISP的表达方式以及含义都有所不同:

    1. 当把“接口”理解成一个类型所提供的所有方法特征的集合的时候,这就是一种逻辑上的概念,接口的划分将直接带来类型的划分。可以把接口理解成角色,一个接口只能代表一个角色,每个角色都有它特定的一个接口,此时,这个原则可以叫做“角色隔离原则”。

    2. 如果把“接口”理解成狭义的特定语言的接口,那么ISP表达的意思是指接口仅仅提供客户端需要的行为,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。在面向对象编程语言中,实现一个接口就需要实现该接口中定义的所有方法,因此大的总接口使用起来不一定很方便,为了使接口的职责单一,需要将大接口中的方法根据其职责不同分别放在不同的小接口中,以确保每个接口使用起来都较为方便,并都承担某一单一角色。接口应该尽量细化,同时接口中的方法应该尽量少,每个接口中只包含一个客户端(如子模块或业务逻辑类)所需的方法即可,这种机制也称为“定制服务”,即为不同的客户端提供宽窄不同的接口。

    在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。

    迪米特法则(Law of Demeter, LoD)

    一个软件实体应当尽可能少地与其他实体发生相互作用。

    如果一个系统符合迪米特法则,那么当其中某一个模块发生修改时,就会尽量少地影响其他模块,扩展会相对容易,这是对软件实体之间通信的限制,迪米特法则要求限制软件实体之间通信的宽度和深度。迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系。

    迪米特法则还有几种定义形式,包括:不要和“陌生人”说话、只与你的直接朋友通信等,在迪米特法则中,对于一个对象,其朋友包括以下几类:

    (1) 当前对象本身(this);
    (2) 以参数形式传入到当前对象方法中的对象;
    (3) 当前对象的成员对象;
    (4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;
    (5) 当前对象所创建的对象。

    迪米特法则要求我们在设计系统时,应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。

    在将迪米特法则运用到系统设计中时,要注意下面的几点:在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;在类的设计上,只要有可能,一个类型应当设计成不变类;在对其他类的引用上,一个对象对其他对象的引用应当降到最低。

    image.png

    组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)

    其实整个设计模式就是在讲如何类与类之间的组合/聚合。

    在一个新的对象里面通过关联关系(包括组合关系和聚合关系)使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用其已有功能的目的。也就是,要尽量使用类的合成复用,尽量不要使用继承。

    如果为了复用,便使用继承的方式将两个不相干的类联系在一起,违反里氏代换原则,哪是生搬硬套,忽略了继承了缺点。

    继承复用破坏数据封装性,将基类的实现细节全部暴露给了派生类,基类的内部细节常常对派生类是透明的,白箱复用;虽然简单,但不安全,不能在程序的运行过程中随便改变;基类的实现发生了改变,派生类的实现也不得不改变;从基类继承而来的派生类是静态的,不可能在运行时间内发生改变,因此没有足够的灵活性。

    展开全文
  • 软件测试的七大原则

    千次阅读 2019-04-20 19:46:04
    1.测试只能证明软件存在缺陷,不能证明软件没有缺陷(证伪不证真)---------测试显示缺陷的存在 2.测试是无法穷举?...4、缺陷的80/20原则(群集效应)(80%的缺陷存在于20%的模块中)。如果测试发现某个模块有...

    1.测试只能证明软件存在缺陷,不能证明软件没有缺陷(证伪不证真)---------测试显示缺陷的存在
    2.测试是无法穷举?(输入数据,处理逻辑路径是无法穷举),学习测试用例的设计鞥新方法-------穷尽测试是不可能的
    3、测试应该尽早测试(发现缺陷和修改的时间越早成本越低。)-----测试的尽早介入
    4、缺陷的80/20原则(群集效应)(80%的缺陷存在于20%的模块中)。如果测试发现某个模块有问题,应当继续深入测试。-----缺陷集群性
    5、杀虫剂悖论(软件对用例会产生免疫力)不断更新测试用例、更新的测试思维-------杀虫剂悖论
    6、测试依赖于商业背景(与行业知识有关) 结合专业和工作经历好准备相关的项目。-----测试活动依赖于测试背景
    7、不存在缺陷的软件并不代表是有用的系统。-----不存在缺陷的谬论

    展开全文
  • 1 软件设计模式的七大原则 1.1设计模式的目的 1.2设计模式七大原则 1.3单一职责原则 1.4 接口隔离原则(Interface Segregation Principle) 1.5 依赖倒转原则 1.6 里氏替换原则 1.7 开闭原则 1.8 迪米特法则 ...

    目录

    1 软件设计模式的七大原则

    1.1设计模式的目的

     1.2 设计模式七大原则

    1.3  单一职责原则

    1.4  接口隔离原则(Interface Segregation Principle)

    1.5 依赖倒转原则

    1.6 里氏替换原则

    1.7 开闭原则

    1.8 迪米特法则

    1.9 合成复用原则(Composite Reuse Principle)

    2 UML 类图

    2.1 UML 基本介绍

    2.2 UML 图

    2.3 UML 类图

    2.4 类图—依赖关系(Dependence)

    2.5 类图—泛化关系(generalization)

    2.7 类图—关联关系(Association)

    2.8 类图—聚合关系(Aggregation)

    2.9 类图—组合关系(Composition)


    1 软件设计模式的七大原则

    1.1设计模式的目的

    编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的 挑战,设计模式是为了让程序(软件),具有更好

    1) 代码重用性 (即:相同功能的代码,不用多次编写)

    2) 可读性 (即:编程规范性, 便于其他程序员的阅读和理解)

    3) 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)

    4) 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)

    5) 使程序呈现高内聚,低耦合的特性

         分享金句:

    6) 设计模式包含了面向对象的精髓,“懂了设计模式,你就懂了面向对象分析和设计(OOA/D)的精要”

    7) Scott Mayers 在其巨著《Effective C++》就曾经说过:C++老手和 C++新手的区别就是前者手背上有很多伤疤

     1.2 设计模式七大原则

      设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么 这样设计的依据)

     设计模式常用的七大原则有:

    1) 单一职责原则

    2) 接口隔离原则

    3) 依赖倒转(倒置)原则

    4) 里氏替换原则

    5) 开闭原则

    6) 迪米特法则

    7) 合成复用原则

    1.3  单一职责原则

       1.3.1 基本介绍

       对类来说的,即一个类应该只负责一项职责。如类 A 负责两个不同职责:职责 1,职责 2。当职责 1 需求变更 而改变 A 时,可能造成职责 2 执行错误,所以需要将类 A 的粒度分解为 A1,A2

     1.3.2应用实例

          1)方案一 分析说明

    public class SingleResponsibility1 {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Vehicle vehicle = new Vehicle();
    		vehicle.run("摩托车");
    		vehicle.run("汽车");
    		vehicle.run("飞机");
    	}
    
    }
    
    // 交通工具类
    // 方式1
    // 1. 在方式1 的run方法中,违反了单一职责原则
    // 2. 解决的方案非常的简单,根据交通工具运行方法不同,分解成不同类即可
    class Vehicle {
    	public void run(String vehicle) {
    		System.out.println(vehicle + " 在公路上运行....");
    	}
    }

    2)方案二 分析说明

     

    public class SingleResponsibility2 {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		RoadVehicle roadVehicle = new RoadVehicle();
    		roadVehicle.run("摩托车");
    		roadVehicle.run("汽车");
    		
    		AirVehicle airVehicle = new AirVehicle();
    		
    		airVehicle.run("飞机");
    	}
    
    }
    
    //方案2的分析
    //1. 遵守单一职责原则
    //2. 但是这样做的改动很大,即将类分解,同时修改客户端
    //3. 改进:直接修改Vehicle 类,改动的代码会比较少=>方案3
    
    class RoadVehicle {
    	public void run(String vehicle) {
    		System.out.println(vehicle + "公路运行");
    	}
    }
    
    class AirVehicle {
    	public void run(String vehicle) {
    		System.out.println(vehicle + "天空运行");
    	}
    }
    
    class WaterVehicle {
    	public void run(String vehicle) {
    		System.out.println(vehicle + "水中运行");
    	}
    }

    3)方案三   分析说明

    package com.atguigu.principle.singleresponsibility;
    
    public class SingleResponsibility3 {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Vehicle2 vehicle2  = new Vehicle2();
    		vehicle2.run("汽车");
    		vehicle2.runWater("轮船");
    		vehicle2.runAir("飞机");
    	}
    
    }
    
    
    //方式3的分析
    //1. 这种修改方法没有对原来的类做大的修改,只是增加方法
    //2. 这里虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责
    class Vehicle2 {
    	public void run(String vehicle) {
    		//处理
    		
    		System.out.println(vehicle + " 在公路上运行....");
    		
    	}
    	
    	public void runAir(String vehicle) {
    		System.out.println(vehicle + " 在天空上运行....");
    	}
    	
    	public void runWater(String vehicle) {
    		System.out.println(vehicle + " 在水中行....");
    	}
    	
    	//方法2.
    	//..
    	//..
    	
    	//...
    }
    

    1.3.3单一职责原则注意事项和细节

    1) 降低类的复杂度,一个类只负责一项职责。

    2) 提高类的可读性,可维护性

    3) 降低变更引起的风险

    4) 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中 方法数量足够少,可以在方法级别保持单一职责原则

    1.4  接口隔离原则(Interface Segregation Principle)

     

    1.4.1基本介绍

    1) 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

    2) 先看一张图:

     

    3) 类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,如果接口 Interface1 对于类 A 和类 C 来说不是最小接口,那么类 B 和类 D 必须去实现他们不需要的方法。

    4) 按隔离原则应当这样处理: 将接口 Interface1 拆分为独立的几个接口(这里我们拆分成 3 个接口),类 A 和类 C 分别与他们需要的接口建立 依赖关系。也就是采用接口隔离原则

     

    1.4.2应用实例

    1) 类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,请编写代码完成此应用实例。

    2) -没有使用接口隔离原则代码

    public class Segregation1 {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    
    	}
    
    }
    
    //接口
    interface Interface1 {
    	void operation1();
    	void operation2();
    	void operation3();
    	void operation4();
    	void operation5();
    }
    
    class B implements Interface1 {
    	public void operation1() {
    		System.out.println("B 实现了 operation1");
    	}
    	
    	public void operation2() {
    		System.out.println("B 实现了 operation2");
    	}
    	public void operation3() {
    		System.out.println("B 实现了 operation3");
    	}
    	public void operation4() {
    		System.out.println("B 实现了 operation4");
    	}
    	public void operation5() {
    		System.out.println("B 实现了 operation5");
    	}
    }
    
    class D implements Interface1 {
    	public void operation1() {
    		System.out.println("D 实现了 operation1");
    	}
    	
    	public void operation2() {
    		System.out.println("D 实现了 operation2");
    	}
    	public void operation3() {
    		System.out.println("D 实现了 operation3");
    	}
    	public void operation4() {
    		System.out.println("D 实现了 operation4");
    	}
    	public void operation5() {
    		System.out.println("D 实现了 operation5");
    	}
    }
    
    class A { //A 类通过接口Interface1 依赖(使用) B类,但是只会用到1,2,3方法
    	public void depend1(Interface1 i) {
    		i.operation1();
    	}
    	public void depend2(Interface1 i) {
    		i.operation2();
    	}
    	public void depend3(Interface1 i) {
    		i.operation3();
    	}
    }
      
    class C { //C 类通过接口Interface1 依赖(使用) D类,但是只会用到1,4,5方法
    	public void depend1(Interface1 i) {
    		i.operation1();
    	}
    	public void depend4(Interface1 i) {
    		i.operation4();
    	}
    	public void depend5(Interface1 i) {
    		i.operation5();
    	}
    }

    1.4.3应传统方法的问题和使用接口隔离原则改进

    1) 类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,如果接口 Interface1 对于类 A 和类 C 来说不是最小接口,那么类 B 和类 D 必须去实现他们不需要的方法

    2) 将接口 Interface1 拆分为独立的几个接口,类 A 和类 C 分别与他们需要的接口建立依赖关系。也就是采用接口 隔离原则

    3) 接口 Interface1 中出现的方法,根据实际情况拆分为三个接口

    public class Segregation1 {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		// 使用一把
    		A a = new A();
    		a.depend1(new B()); // A类通过接口去依赖B类
    		a.depend2(new B());
    		a.depend3(new B());
    
    		C c = new C();
    
    		c.depend1(new D()); // C类通过接口去依赖(使用)D类
    		c.depend4(new D());
    		c.depend5(new D());
    
    	}
    
    }
    
    // 接口1
    interface Interface1 {
    	void operation1();
    
    }
    
    // 接口2
    interface Interface2 {
    	void operation2();
    
    	void operation3();
    }
    
    // 接口3
    interface Interface3 {
    	void operation4();
    
    	void operation5();
    }
    
    class B implements Interface1, Interface2 {
    	public void operation1() {
    		System.out.println("B 实现了 operation1");
    	}
    
    	public void operation2() {
    		System.out.println("B 实现了 operation2");
    	}
    
    	public void operation3() {
    		System.out.println("B 实现了 operation3");
    	}
    
    }
    
    class D implements Interface1, Interface3 {
    	public void operation1() {
    		System.out.println("D 实现了 operation1");
    	}
    
    	public void operation4() {
    		System.out.println("D 实现了 operation4");
    	}
    
    	public void operation5() {
    		System.out.println("D 实现了 operation5");
    	}
    }
    
    class A { // A 类通过接口Interface1,Interface2 依赖(使用) B类,但是只会用到1,2,3方法
    	public void depend1(Interface1 i) {
    		i.operation1();
    	}
    
    	public void depend2(Interface2 i) {
    		i.operation2();
    	}
    
    	public void depend3(Interface2 i) {
    		i.operation3();
    	}
    }
    
    class C { // C 类通过接口Interface1,Interface3 依赖(使用) D类,但是只会用到1,4,5方法
    	public void depend1(Interface1 i) {
    		i.operation1();
    	}
    
    	public void depend4(Interface3 i) {
    		i.operation4();
    	}
    
    	public void depend5(Interface3 i) {
    		i.operation5();
    	}
    }

    1.5 依赖倒转原则

    1.5.1基本介绍

    依赖倒转原则(Dependence Inversion Principle)是指:

    1) 高层模块不应该依赖低层模块,二者都应该依赖其抽象

    2) 抽象不应该依赖细节,细节应该依赖抽象

    3) 依赖倒转(倒置)的中心思想是面向接口编程

    4) 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架 构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类

    5) 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

    1.5.2应用实例 请编程完成 Person 接收消息 的功能。

    1) 实现方案 1 + 分析说明

    public class DependecyInversion {
    
    	public static void main(String[] args) {
    		Person person = new Person();
    		person.receive(new Email());
    	}
    
    }
    
    
    class Email {
    	public String getInfo() {
    		return "电子邮件信息: hello,world";
    	}
    }
    
    //完成Person接收消息的功能
    //方式1分析
    //1. 简单,比较容易想到
    //2. 如果我们获取的对象是 微信,短信等等,则新增类,同时Perons也要增加相应的接收方法
    //3. 解决思路:引入一个抽象的接口IReceiver, 表示接收者, 这样Person类与接口IReceiver发生依赖
    //   因为Email, WeiXin 等等属于接收的范围,他们各自实现IReceiver 接口就ok, 这样我们就符号依赖倒转原则
    class Person {
    	public void receive(Email email ) {
    		System.out.println(email.getInfo());
    	}
    }
    

    2) 实现方案 2(依赖倒转)

    public class DependecyInversion {
    
    	public static void main(String[] args) {
    		//客户端无需改变
    		Person person = new Person();
    		person.receive(new Email());
    		
    		person.receive(new WeiXin());
    	}
    
    }
    
    //定义接口
    interface IReceiver {
    	public String getInfo();
    }
    
    class Email implements IReceiver {
    	public String getInfo() {
    		return "电子邮件信息: hello,world";
    	}
    }
    
    //增加微信
    class WeiXin implements IReceiver {
    	public String getInfo() {
    		return "微信信息: hello,ok";
    	}
    }
    
    //方式2
    class Person {
    	//这里我们是对接口的依赖
    	public void receive(IReceiver receiver ) {
    		System.out.println(receiver.getInfo());
    	}
    }
    

    1.5.3依赖关系传递的三种方式和应用案例

    1) 接口传递 

    2) 构造方法传递 

    3) setter 方式传递

    4)应用案例

    public class DependencyPass {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		ChangHong changHong = new ChangHong();
    //		OpenAndClose openAndClose = new OpenAndClose();
    //		openAndClose.open(changHong);
    		
    		//通过构造器进行依赖传递
    //		OpenAndClose openAndClose = new OpenAndClose(changHong);
    //		openAndClose.open();
    		//通过setter方法进行依赖传递
    		OpenAndClose openAndClose = new OpenAndClose();
    		openAndClose.setTv(changHong);
    		openAndClose.open();
    
    	}
    
    }
    
    // 方式1: 通过接口传递实现依赖
    // 开关的接口
    // interface IOpenAndClose {
    // public void open(ITV tv); //抽象方法,接收接口
    // }
    //
    // interface ITV { //ITV接口
    // public void play();
    // }
    // 
    // class ChangHong implements ITV {
    //
    //	@Override
    //	public void play() {
    //		// TODO Auto-generated method stub
    //		System.out.println("长虹电视机,打开");
    //	}
    //	 
    // }
     实现接口
    // class OpenAndClose implements IOpenAndClose{
    // public void open(ITV tv){
    // tv.play();
    // }
    // }
    
    // 方式2: 通过构造方法依赖传递
    // interface IOpenAndClose {
    // public void open(); //抽象方法
    // }
    // interface ITV { //ITV接口
    // public void play();
    // }
    // class OpenAndClose implements IOpenAndClose{
    // public ITV tv; //成员
    // public OpenAndClose(ITV tv){ //构造器
    // this.tv = tv;
    // }
    // public void open(){
    // this.tv.play();
    // }
    // }
    
    
    // 方式3 , 通过setter方法传递
    interface IOpenAndClose {
    	public void open(); // 抽象方法
    
    	public void setTv(ITV tv);
    }
    
    interface ITV { // ITV接口
    	public void play();
    }
    
    class OpenAndClose implements IOpenAndClose {
    	private ITV tv;
    
    	public void setTv(ITV tv) {
    		this.tv = tv;
    	}
    
    	public void open() {
    		this.tv.play();
    	}
    }
    
    class ChangHong implements ITV {
    
    	@Override
    	public void play() {
    		// TODO Auto-generated method stub
    		System.out.println("长虹电视机,打开");
    	}
    	 
    }

     

    1.5.4依赖倒转原则的注意事项和细节

    1) 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.

    2) 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展 和优化

    3) 继承时遵循里氏替换原则

    1.6 里氏替换原则

    1.6.1  OO 中的继承性的思考和说明

    1) 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有 的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。

    2) 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低, 增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且 父类修改后,所有涉及到子类的功能都有可能产生故障

    3) 问题提出:在编程中,如何正确的使用继承? => 里氏替换原则

    1.6.2基本介绍

    1) 里氏替换原则(Liskov Substitution Principle)在 1988 年,由麻省理工学院的以为姓里的女士提出的。

    2) 如果对每个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都 代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。换句话说,所有引用基类的地 方必须能透明地使用其子类的对象。

    3) 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法

    4) 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来 解决问题。

    1.6.3一个程序引出的问题和思考

    该看个程序, 思考下问题和解决思路

    public class Liskov {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		A a = new A();
    		System.out.println("11-3=" + a.func1(11, 3));
    		System.out.println("1-8=" + a.func1(1, 8));
    
    		System.out.println("-----------------------");
    		B b = new B();
    		System.out.println("11-3=" + b.func1(11, 3));//这里本意是求出11-3
    		System.out.println("1-8=" + b.func1(1, 8));// 1-8
    		System.out.println("11+3+9=" + b.func2(11, 3));
    		
    		
    
    	}
    
    }
    
    // A类
    class A {
    	// 返回两个数的差
    	public int func1(int num1, int num2) {
    		return num1 - num2;
    	}
    }
    
    // B类继承了A
    // 增加了一个新功能:完成两个数相加,然后和9求和
    class B extends A {
    	//这里,重写了A类的方法, 可能是无意识
    	public int func1(int a, int b) {
    		return a + b;
    	}
    
    	public int func2(int a, int b) {
    		return func1(a, b) + 9;
    	}
    }
    

    1.6.4解决方法

    1) 我们发现原来运行正常的相减功能发生了错误。原因就是类 B 无意中重写了父类的方法,造成原有功能出现错 误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候

    2) 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等 关系代替.

    3) 改进方案

    代码实现

    ublic class Liskov {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		A a = new A();
    		System.out.println("11-3=" + a.func1(11, 3));
    		System.out.println("1-8=" + a.func1(1, 8));
    
    		System.out.println("-----------------------");
    		B b = new B();
    		//因为B类不再继承A类,因此调用者,不会再func1是求减法
    		//调用完成的功能就会很明确
    		System.out.println("11+3=" + b.func1(11, 3));//这里本意是求出11+3
    		System.out.println("1+8=" + b.func1(1, 8));// 1+8
    		System.out.println("11+3+9=" + b.func2(11, 3));
    		
    		
    		//使用组合仍然可以使用到A类相关方法
    		System.out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3
    		
    
    	}
    
    }
    
    //创建一个更加基础的基类
    class Base {
    	//把更加基础的方法和成员写到Base类
    }
    
    // A类
    class A extends Base {
    	// 返回两个数的差
    	public int func1(int num1, int num2) {
    		return num1 - num2;
    	}
    }
    
    // B类继承了A
    // 增加了一个新功能:完成两个数相加,然后和9求和
    class B extends Base {
    	//如果B需要使用A类的方法,使用组合关系
    	private A a = new A();
    	
    	//这里,重写了A类的方法, 可能是无意识
    	public int func1(int a, int b) {
    		return a + b;
    	}
    
    	public int func2(int a, int b) {
    		return func1(a, b) + 9;
    	}
    	
    	//我们仍然想使用A的方法
    	public int func3(int a, int b) {
    		return this.a.func1(a, b);
    	}
    }
    

     

    1.7 开闭原则

    1.7.1基本介绍

    1) 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则

    2) 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实 现扩展细节。

    3) 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

    4) 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

    1.7.2看下面一段代码

      看一个画图形的功能。 类图设计,如下:

    代码演示

    public class Ocp {
    
    	public static void main(String[] args) {
    		//使用看看存在的问题
    		GraphicEditor graphicEditor = new GraphicEditor();
    		graphicEditor.drawShape(new Rectangle());
    		graphicEditor.drawShape(new Circle());
    		graphicEditor.drawShape(new Triangle());
    	}
    
    }
    
    //这是一个用于绘图的类 [使用方]
    class GraphicEditor {
    	//接收Shape对象,然后根据type,来绘制不同的图形
    	public void drawShape(Shape s) {
    		if (s.m_type == 1)
    			drawRectangle(s);
    		else if (s.m_type == 2)
    			drawCircle(s);
    		else if (s.m_type == 3)
    			drawTriangle(s);
    	}
    
    	//绘制矩形
    	public void drawRectangle(Shape r) {
    		System.out.println(" 绘制矩形 ");
    	}
    
    	//绘制圆形
    	public void drawCircle(Shape r) {
    		System.out.println(" 绘制圆形 ");
    	}
    	
    	//绘制三角形
    	public void drawTriangle(Shape r) {
    		System.out.println(" 绘制三角形 ");
    	}
    }
    
    //Shape类,基类
    class Shape {
    	int m_type;
    }
    
    class Rectangle extends Shape {
    	Rectangle() {
    		super.m_type = 1;
    	}
    }
    
    class Circle extends Shape {
    	Circle() {
    		super.m_type = 2;
    	}
    }
    
    //新增画三角形
    class Triangle extends Shape {
    	Triangle() {
    		super.m_type = 3;
    	}
    }
    

    1.7.3方式 1 的优缺点

    1) 优点是比较好理解,简单易操作。

    2) 缺点是违反了设计模式的 ocp 原则,即对扩展开放(提供方),对修改关闭(使用方)。即当我们给类增加新功能的 时候,尽量不修改代码,或者尽可能少修改代码.

    3) 比如我们这时要新增加一个图形种类 三角形,我们需要做如下修改,修改的地方较多

    4) 代码演示

    方式 1 的改进的思路分析

    1.7.4改进的思路分析

    思路:把创建 Shape 类做成抽象类,并提供一个抽象的 draw 方法,让子类去实现即可,这样我们有新的图形 种类时,只需要让新的图形类继承 Shape,并实现 draw 方法即可,使用方的代码就不需要修 -> 满足了开闭原则

    改进后的代码:

    public class Ocp {
    
    	public static void main(String[] args) {
    		//使用看看存在的问题
    		GraphicEditor graphicEditor = new GraphicEditor();
    		graphicEditor.drawShape(new Rectangle());
    		graphicEditor.drawShape(new Circle());
    		graphicEditor.drawShape(new Triangle());
    		graphicEditor.drawShape(new OtherGraphic());
    	}
    
    }
    
    //这是一个用于绘图的类 [使用方]
    class GraphicEditor {
    	//接收Shape对象,调用draw方法
    	public void drawShape(Shape s) {
    		s.draw();
    	}
    
    	
    }
    
    //Shape类,基类
    abstract class Shape {
    	int m_type;
    	
    	public abstract void draw();//抽象方法
    }
    
    class Rectangle extends Shape {
    	Rectangle() {
    		super.m_type = 1;
    	}
    
    	@Override
    	public void draw() {
    		// TODO Auto-generated method stub
    		System.out.println(" 绘制矩形 ");
    	}
    }
    
    class Circle extends Shape {
    	Circle() {
    		super.m_type = 2;
    	}
    	@Override
    	public void draw() {
    		// TODO Auto-generated method stub
    		System.out.println(" 绘制圆形 ");
    	}
    }
    
    //新增画三角形
    class Triangle extends Shape {
    	Triangle() {
    		super.m_type = 3;
    	}
    	@Override
    	public void draw() {
    		// TODO Auto-generated method stub
    		System.out.println(" 绘制三角形 ");
    	}
    }
    
    //新增一个图形
    class OtherGraphic extends Shape {
    	OtherGraphic() {
    		super.m_type = 4;
    	}
    
    	@Override
    	public void draw() {
    		// TODO Auto-generated method stub
    		System.out.println(" 绘制其它图形 ");
    	}
    }
    

    1.8 迪米特法则

    1.8.1基本介绍

    1) 一个对象应该对其他对象保持最少的了解

    2) 类与类关系越密切,耦合度越大

    3) 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于 被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息

    4) 迪米特法则还有个更简单的定义:只与直接的朋友通信

    5) 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间 是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返 回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变 量的形式出现在类的内部。

    1.8.2应用实例

    1) 有一个学校,下属有各个学院和总部,现要求打印出学校总部员工 ID 和学院员工的 id

    2) 编程实现上面的功能, 看代码演示

    3) 代码演示

    //客户端
    public class Demeter1 {
    
    	public static void main(String[] args) {
    		//创建了一个 SchoolManager 对象
    		SchoolManager schoolManager = new SchoolManager();
    		//输出学院的员工id 和  学校总部的员工信息
    		schoolManager.printAllEmployee(new CollegeManager());
    
    	}
    
    }
    
    
    //学校总部员工类
    class Employee {
    	private String id;
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getId() {
    		return id;
    	}
    }
    
    
    //学院的员工类
    class CollegeEmployee {
    	private String id;
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getId() {
    		return id;
    	}
    }
    
    
    //管理学院员工的管理类
    class CollegeManager {
    	//返回学院的所有员工
    	public List<CollegeEmployee> getAllEmployee() {
    		List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
    		for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
    			CollegeEmployee emp = new CollegeEmployee();
    			emp.setId("学院员工id= " + i);
    			list.add(emp);
    		}
    		return list;
    	}
    }
    
    //学校管理类
    
    //分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
    //CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则 
    class SchoolManager {
    	//返回学校总部的员工
    	public List<Employee> getAllEmployee() {
    		List<Employee> list = new ArrayList<Employee>();
    		
    		for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list
    			Employee emp = new Employee();
    			emp.setId("学校总部员工id= " + i);
    			list.add(emp);
    		}
    		return list;
    	}
    
    	//该方法完成输出学校总部和学院员工信息(id)
    	void printAllEmployee(CollegeManager sub) {
    		
    		//分析问题
    		//1. 这里的 CollegeEmployee 不是  SchoolManager的直接朋友
    		//2. CollegeEmployee 是以局部变量方式出现在 SchoolManager
    		//3. 违反了 迪米特法则 
    		
    		//获取到学院员工
    		List<CollegeEmployee> list1 = sub.getAllEmployee();
    		System.out.println("------------学院员工------------");
    		for (CollegeEmployee e : list1) {
    			System.out.println(e.getId());
    		}
    		//获取到学校总部员工
    		List<Employee> list2 = this.getAllEmployee();
    		System.out.println("------------学校总部员工------------");
    		for (Employee e : list2) {
    			System.out.println(e.getId());
    		}
    	}
    }
    

     

    1.8.3应用实例改进

    1) 前面设计的问题在于 SchoolManager 中,CollegeEmployee 类并不是 SchoolManager 类的直接朋友 (分析)

    2) 按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合

    3) 对代码按照迪米特法则 进行改进.

    4) 代码演示

    //客户端
    public class Demeter1 {
    
    	public static void main(String[] args) {
    		System.out.println("~~~使用迪米特法则的改进~~~");
    		//创建了一个 SchoolManager 对象
    		SchoolManager schoolManager = new SchoolManager();
    		//输出学院的员工id 和  学校总部的员工信息
    		schoolManager.printAllEmployee(new CollegeManager());
    
    	}
    
    }
    
    
    //学校总部员工类
    class Employee {
    	private String id;
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getId() {
    		return id;
    	}
    }
    
    
    //学院的员工类
    class CollegeEmployee {
    	private String id;
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getId() {
    		return id;
    	}
    }
    
    
    //管理学院员工的管理类
    class CollegeManager {
    	//返回学院的所有员工
    	public List<CollegeEmployee> getAllEmployee() {
    		List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
    		for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
    			CollegeEmployee emp = new CollegeEmployee();
    			emp.setId("学院员工id= " + i);
    			list.add(emp);
    		}
    		return list;
    	}
    	
    	//输出学院员工的信息
    	public void printEmployee() {
    		//获取到学院员工
    		List<CollegeEmployee> list1 = getAllEmployee();
    		System.out.println("------------学院员工------------");
    		for (CollegeEmployee e : list1) {
    			System.out.println(e.getId());
    		}
    	}
    }
    
    //学校管理类
    
    //分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
    //CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则 
    class SchoolManager {
    	//返回学校总部的员工
    	public List<Employee> getAllEmployee() {
    		List<Employee> list = new ArrayList<Employee>();
    		
    		for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list
    			Employee emp = new Employee();
    			emp.setId("学校总部员工id= " + i);
    			list.add(emp);
    		}
    		return list;
    	}
    
    	//该方法完成输出学校总部和学院员工信息(id)
    	void printAllEmployee(CollegeManager sub) {
    		
    		//分析问题
    		//1. 将输出学院的员工方法,封装到CollegeManager
    		sub.printEmployee();
    	
    		//获取到学校总部员工
    		List<Employee> list2 = this.getAllEmployee();
    		System.out.println("------------学校总部员工------------");
    		for (Employee e : list2) {
    			System.out.println(e.getId());
    		}
    	}
    }
    

     

    1.8.4迪米特法则注意事项和细节

    1) 迪米特法则的核心是降低类之间的耦合

    2) 但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是 要求完全没有依赖关系

    1.9 合成复用原则(Composite Reuse Principle)

    1.9.1基本介绍 原则是尽量使用合成/聚合的方式,而不是使用继承

     

    1.10 设计原则核心思想

    1) 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

    2) 针对接口编程,而不是针对实现编程。

    3) 为了交互对象之间的松耦合设计而努力

    2 UML 类图

    2.1 UML 基本介绍

    1) UML——Unified modeling language UML (统一建模语言),是一种用于软件系统分析和设计的语言工具,它用 于帮助软件开发人员进行思考和记录思路的结果

    2) UML 本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他 们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等,如下图:

     3) 使用 UML 来建模,常用的工具有 Rational Rose , 也可以使用一些插件来建模 Eclipse 安装 UML插件       (AmaterasUML).zip

     

    2.2 UML 图

    画 UML 图与写文章差不多,都是把自己的思想描述给别人看,关键在于思路和条理,UML 图分类:

    1) 用例图(use case)

    2) 静态结构图:类图、对象图、包图、组件图、部署图

    3) 动态行为图:交互图(时序图与协作图)、状态图、活动图

    说明:

    1) 类图是描述类与类之间的关系的,是 UML 图中最核心的

    2) 在讲解设计模式时,我们必然会使用类图,,需要先给大家讲解类图

    2.3 UML 类图

    1) 用于描述系统中的类(对象)本身的组成和类(对象)之间的各种静态关系。

    2) 类之间的关系:依赖、泛化(继承)、实现、关联、聚合与组合。

    3) 类图简单举例

    public class Person{ //代码形式->类图
    private Integer id;
    private String name;
    public void setName(String name){
    this.name=name;
    }
    public String getName(){
    return name;
    }
    }

    2.4 类图—依赖关系(Dependence)

    只要是在类中用到了对方,那么他们之间就存在依赖关系。如果没有对方,连编绎都通过不了。

    public class PersonServiceBean {
    private PersonDao personDao;//类
    public void save(Person person){}
    public IDCard getIDCard(Integer personid){}
    public void modify(){
    Department department = new Department();
    }
    }
    public class PersonDao{}
    public class IDCard{}
    public class Person{}
    public class Department{}

    对应的类图:

    小结

    1) 类中用到了对方

    2) 如果是类的成员属性

    3) 如果是方法的返回类型

    4) 是方法接收的参数类型

    5) 方法中使用到

    2.5 类图—泛化关系(generalization)

    泛化关系实际上就是继承关系,它是依赖关系的特例

    public abstract class DaoSupport{
    public void save(Object entity){
    }
    public void delete(Object id){
    }
    }
    public class PersonServiceBean extends Daosupport{
    }

    对应的类图

    小结:

    1) 泛化关系实际上就是继承关系

    2) 如果 A 类继承了 B 类,我们就说 A 和 B 存在泛化关系

    2.7 类图—关联关系(Association)

    2.8 类图—聚合关系(Aggregation)

    2.8.1基本介绍

    聚合关系(Aggregation)表示的是整体和部分的关系,整体与部分可以分开。聚合关系是关联关系的特例,所以他具有关联的导航性与多重性。 如:一台电脑由键盘(keyboard)、显示器(monitor),鼠标等组成;组成电脑的各个配件是可以从电脑上分离出来 的,使用带空心菱形的实线来表示:

    2.8.2应用实例

     

    2.9 类图—组合关系(Composition)

    2.9.1基本介绍 组合关系:也是整体与部分的关系,但是整体与部分不可以分开。

    再看一个案例:在程序中我们定义实体:Person 与 IDCard、Head, 那么 Head 和 Person 就是 组合,IDCard 和 Person 就是聚合。 但是如果在程序中 Person 实体中定义了对 IDCard 进行级联删除,即删除 Person 时连同 IDCard 一起删除,那 么 IDCard 和 Person 就是组合了.

    2.9.2应用案例

    public class Person{
    private IDCard card;
    private Head head = new Head();
    }
    public class IDCard{}
    public class Head{}

    对应的类图:

    案例 2

    public class Computer {
    private Mouse mouse = new Mouse(); //鼠标可以和 computer 不能分离
    private Moniter moniter = new Moniter();//显示器可以和 Computer 不能分离
    public void setMouse(Mouse mouse) {
    this.mouse = mouse;
    }
    public void setMoniter(Moniter moniter) {
    this.moniter = moniter;
    }
    }
    public class Mouse {
    }
    public class Moniter {
    }

    对应的类图:

     

    到此完毕啦啦啦啦啦!!!!!!!!!!!

    展开全文
  • 软件体系结构七大设计原则,开闭原则 里氏代换原则 依赖倒转原则 迪米特法则 迪米特法则
  • 软件设计原则-面向对象设计七大原则.zip 面向对象和C语言完全不是一种思考问题的方式,面向对象是种哲学思想,只有明白了其核心思想,才能以不变应万变 只有吃透面向对象的七大设计原则,比如:依赖倒置原则,迪米特法则等...
  • 面向对象设计七大原则

    千次阅读 2018-06-07 17:28:00
    然后对面向对象设计的一些原则进行了一些学习和整理。包括SOLID、合成复用原则与迪米特法则。可维护性Robert C.Martin认为⼀个可维护性较低的软件设计,通常由于如下四个原因造成:• 过于僵硬(Rigidity)• 过于脆弱...
  • 面向对象编程七大原则

    千次阅读 2017-10-30 13:31:06
    1. 开闭原则(Open-Closed Principle, OCP) 定义:软件实体应当对扩展开放,对修改关闭。这句话说得有点专业,更通俗一点讲,也就是:软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能...
  • 硬盘分区七大原则.docx
  • 面向对象七大原则

    2012-10-22 23:34:33
    面向对象七大原则
  • 设计模式-七大原则(图解一目了然)

    千次阅读 多人点赞 2020-09-30 20:32:44
    你是不是还在这样写代码?当头一棒,看完本文快回去检查检查你的代码吧! 单一职责原则 接口隔离原则 依赖倒转原则 里氏替换原则 开闭原则 迪米特法则 合成复用原则
  • 软件架构设计的七大原则(附架构资料) 程序媛菲儿2019-06-24 19:05:29 一、开闭原则 开闭原则(Open-Closed Principle OCP)是指一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。所谓的开始,是用抽象...
  • 七大原则&&六大关系

    2011-10-30 15:46:53
    七大原则&&六大关系 开-闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开发,对修改关闭.说的是,再设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展.换言之,应当可以在不必修改源代码的...
  • 面向对象之七大原则

    千次阅读 2018-07-18 17:27:49
    单一职责,里氏替换,迪米特法则,依赖倒转,接口隔离,合成/聚合原则,开放-封闭 。 1. 开闭原则(Open-Closed Principle, OCP) 定义:软件实体应当对扩展开放,对修改关闭。这句话说得有点专业,更通俗一点讲,...
  • 最通俗易懂--设计模式之七大原则

    千次阅读 2017-03-09 20:52:45
    我写的这一系列设计模式,可能会围绕Java+Android来举例,但是不影响阅读。...好了,至此设计模式七大原则告一段落,我写的不是最官方,但确保通俗易懂,有错就改,共同勉励,加油!接下来23种设计模式,一个一个来。
  • java设计模式七大原则

    千次阅读 2018-06-05 10:26:33
    当一个接口太时,我们需要把他拆分成更小的接口,但不能违反单一职责原则,每个接口应该承担一种相对独立的角色,不该干的事情不干,该干的事情都要干。 6.迪米特法则 ( Law Of Demeter )   -一个实体应当尽量...
  • 面向对象七大原则——肖文斌.pdf
  • 设计软件的几个原则,这个也是设计模式的精髓所在
  • OO七大原则

    千次阅读 2016-11-11 12:27:34
    OO七大原则为:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、合成/聚合复用原则、迪米特法则 1、单一职责原则SRP(Simple Responsibility Principle): 单一职责原则,就是一个设计元素只...
  • 软件设计的七大原则

    万次阅读 2018-12-15 22:07:45
    七大设计原则 开闭原则 依赖导倒置原则 单一职责原则 接口隔离原则 迪米特原则 里氏替换原则 合成复用原则 设计模式-创建型模式 工厂方法模式 抽象工厂模式 建造者模式 单例模式 原型模式 设计模式-...
  • 23中设计模式,是前任的经验总结,但是在这个原理的背后,还有原理在支撑,他们就是下面要介绍的7大原则,明白了这些原则,非常有用 a.面试时经常被问到。b.有助于对设计模式的学习。c.有助于自己的设计思想的养成与...
  • 质量管理七大原则在碳纤维生产企业管理中的应用

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 134,037
精华内容 53,614
关键字:

七大原则