zoukankan      html  css  js  c++  java
  • 面向对象的特征和设计原则

    转载自:https://blog.csdn.net/rankun1/article/details/50789571 以及https://www.cnblogs.com/corvoh/p/5747856.html

    面向对象三大特征【转载】

    封装

      把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

      这段话我觉得描述封装非常好:基本的变量已经不再浮游于一大段一大段的程序中了,它们已经放弃了(其实是程序员不用这种方式了)这种自由自在的存在方式,而是安稳的寄 居于庞大而蹒跚的“对象”内部,与外界隔开来,通过迂回曲折的间接途径与外部世界联系和通信。而这些对象,就是它们这些基本变量的生存机器!在面向过程的开发中,变量被暴露在整个程序中,不小心的一个修改就可能导致整个程序出错。 所以封装有利于我们让自己的程序更健壮。

    继承 

    面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
    通过继承创建的新类称为“子类”或“派生类”。
    被继承的类称为“基类”、“父类”或“超类”。
    继承的过程,就是从一般到特殊的过程。
    要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
    在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
     
    继承概念的实现方式有三类:实现继承、接口继承和可视继承。
    Ø         实现继承是指使用基类的属性和方法而无需额外编码的能力;
    Ø         接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
    Ø         可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
    在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
    抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。
    OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

    多态

      多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
    实现多态,有二种方式,覆盖,重载。
      覆盖,是指子类重新定义父类的虚函数的做法。
      重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
      其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”
    那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

    UML概念讲解

      泛化:体现在编程语言中,就是继承,而泛化是UML中的一个术语。假设类A和类B,若在逻辑上类B是类A的一种(比如男人是一种人),则允许类B继承类A的功能和属性。

      组合:类A和类B,如果逻辑上类A是类B的一部分(a part of),则不允许类B从类A派生,例如眼睛、鼻子、口、耳都是头的一部分,那么头是不能从其中任何一个继承的,但是眼睛、鼻子、口、耳则可以组合出头。

      聚合:聚合的类型分为无、共享(聚合)、复合(组合)三种。

      共享(聚合:aggregation):在UML中使用空心菱形表示聚合关系,聚合是一种has a的关系,如下图所示:

       聚合是一种相对松散的方式,聚合类B不需要对被聚合的类A负责。就像你的铅笔盒,里面有钢笔、铅笔、中性笔。但是这些笔不一定必须有,你买来可以放在里面,但是铅笔盒并不对你有没有这个笔负责。
      组合(composition):是一种更加坚固的聚合方式(复合),就是像一辆汽车,没有车轮就不能称之为汽车。组合表示的关系也是has-a,不过在这里,A的生命期受B控制。即A会随着B的创建而创建,随B的消亡而消亡。它在UML类图中的表示方式如下:用实心的菱形表示
      
      依赖(Dependency),在UML中的表示如下:

       这表示一种依赖关系,意味着类A被修改,类B将受到影响。

    面向对象的设计原则

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

      是指一个类的功能要单一,不能包罗万象。(就一个类而言,应该仅有一个引起它变化的原因)。

    开放-封闭原则OCP(Open-Close Principle) 

      是说软件实体(类、模块、函数等等)应该可以扩展,但是不可更改。目的是在面对需求的变更时,能够使系统保持相对稳定,可以进行版本的迭代。

    Liskov替换原则(Liskov-Substituion Principle)

      其核心思想是:子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。在父类和子类的具体行为中,必须严格把握继承层次中的关系和特征,将基类替换为子类,程序的行为不会发生任何变化。同时,这一约束反过来则是不成立的,子类可以替换基类,但是基类不一定能替换子类。
      Liskov替换原则,主要着眼于对抽象和多态建立在继承的基础上,因此只有遵循了Liskov替换原则,才能保证继承复用是可靠地。实现的方法是面向接口编程:将公共部分抽象为基类接口或抽象类,通过Extract Abstract Class,在子类中通过覆写父类的方法实现新的方式支持同样的职责。
      Liskov替换原则是关于继承机制的设计原则,违反了Liskov替换原则就必然导致违反开放封闭原则。
      Liskov替换原则能够保证系统具有良好的拓展性,同时实现基于多态的抽象机制,能够减少代码冗余,避免运行期的类型判别。

    依赖倒置原则(Dependecy-Inversion Principle)

      其实就是针对接口编程,其核心思想是:高层模块不应该依赖低层模块,两个都应该依赖抽象。抽象不应该依赖袭击。细节应该依赖抽象。例如:为了方便使用数据库,我们把常用代码封装成函数库,这样在做项目时,需要访问数据库就去调用这些低层函数就可以,这就叫做高层依赖低层。但是现在,如果新的项目,我们发现高层的业务逻辑是一致的,但是客户希望使用另外的数据库,这样我们就没办法复用这些高层模块了,因为它和具体的数据库捆绑在了一起。那么处理办法就是在设计时,将低层和高层解耦,低层向高层提供稳定的接口,那么只要接口不发生变化,任何的更改都不担心引起其它不好的影响。而此时低层模块和高层模块都应该依赖于抽象,简单的说就是依赖接口或者抽象类。这样无论是低层模块还是高层模块都能得到复用。

    迪米特法则(Lod)----这不是面向对象的5个设计原则之一

      也叫最小知识原则。如果两个类不必彼此直接通信,那么这两个类就不应单发生直接的相互作用。如果其中一个类需要调用另一个类的某个方法的话,可以通过第三者转发这个调用。首先,在类的结构设计上,每一个类应该尽量降低成员的访问权限。迪米特法则的根本就是强调了类之间的松耦合。

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

      其核心思想是:使用多个小的专门的接口,而不要使用一个大的总接口。
       具体而言,接口隔离原则体现在:接口应该是内聚的,应该避免“胖”接口。一个类对另外一个类的依赖应该建立在最小的接口上,不要强迫依赖不用的方法,这是一种接口污染。
            接口有效地将细节和抽象隔离,体现了对抽象编程的一切好处,接口隔离强调接口的单一性。而胖接口存在明显的弊端,会导致实现的类型必须完全实现接口的所有方法、属性等;而某些时候,实现类型并非需要所有的接口定义,在设计上这是“浪费”,而且在实施上这会带来潜在的问题,对胖接口的修改将导致一连串的客户端程序需要修改,有时候这是一种灾难。在这种情况下,将胖接口分解为多个特点的定制化方法,使得客户端仅仅依赖于它们的实际调用的方法,从而解除了客户端不会依赖于它们不用的方法。
        分离的手段主要有以下两种:1、委托分离,通过增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统的开销。2、多重继承分离,通过接口多继承来实现客户的需求,这种方式是较好的。

    这个原则的意思是:使用多个专门的接口比使用单个接口要好的多!

      为了减少接口的定义,将许多类似的方法都放在一个接口中,最后发现,维护和实现接口的时候花了太多精力,而接口所定义的操作相当于对客户端的一种承诺,这种承诺当然是越少越好,越精练越好,过多的承诺带来的就是你的大量精力和时间去维护!

    模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来。

    耦合

      简单地说,软件工程中对象之间的耦合度就是对象之间的依赖性。指导使用和维护对象的主要问题是对象之间的多重依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。

      有软硬件之间的耦合,还有软件各模块之间的耦合。
      耦合性是程序结构中各个模块之间相互关联的度量。它取决于各个模块之间的接口的复杂程度、调用模块的方式以及哪些信息通过接口。

      耦合可以分为以下几种,它们之间的耦合度由高到低排列如下:

    • 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
    • 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
    • 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
    • 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
    • 标记耦合 。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
    • 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
    • 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。

    总结
    耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。

     内聚和耦合(引用自百度百科

    耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息
    内聚性:又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。
    所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。
    耦合:一个软件结构内不同模块之间互连程度的度量。
    对于低耦合,粗浅的理解是:一个完整的系统,模块与模块之间,尽可能的使其独立存在。也就是说,让每个模块,尽可能的独立完成某个特定的子功能。模块与模块之间的接口,尽量的少而简单。如果某两个模块间的关系比较复杂的话,最好首先考虑进一步的模块划分。这样有利于修改和组合。 [1] 
     
  • 相关阅读:
    ie6 ie7 ie8 ie9 firefox css hack
    小型数据分页
    AsyncTask 使用须知
    调用startActivityForResult,onActivityResult无响应的问题
    Android之背景图片设置为重复而不是默认的拉伸
    Android Service之Messenger实现通信
    android之.9.png图片应用
    小米2及其他Android手机无法连接mac解决方案
    纯CSS3打造滑动下拉导航菜单
    DIV制作气泡对话框 兼容IE6
  • 原文地址:https://www.cnblogs.com/ToBeExpert/p/9670435.html
Copyright © 2011-2022 走看看