设计模式的预备知识:类图与设计原则

1 UML类图

(转自:http://blog.csdn.net/tianhai110/article/details/6339565

在UML类图中,常见的有以下几种关系:

  • 泛化(Generalization)
  • 实现(Realization)
  • 关联(Association)
  • 聚合(Aggregation)
  • 组合(Composition)
  • 依赖(Dependency)

1.1 泛化(Generalization)

【泛化关系、继承关系、A is-a B、A extends B、A -> B】:是一种继承关系,它指定了子类如何特化父类的所有特征和行为例如:老虎是动物的一种.
【箭头指向】:带三角箭头的实线,箭头指向父类

这里写图片描述


1.2 实现(Realization)

【实现关系,A implements B、A —> B】:是一种类与接口的关系,表示类是接口所有特征和行为的实现
【箭头指向】:带三角箭头的虚线,箭头指向接口

这里写图片描述


1.3 关联(Association)

【关联关系,A has-a B,A —> B】:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子。

关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。

A类中有一个成员变量属于B类,则A关联B,A—>B。

【代码体现】:成员变量
【箭头及指向】:带普通箭头的实心线,指向被拥有者

这里写图片描述

上图中,老师与学生是双向关联,老师有多名学生,学生也可能有多名老师。但学生与某课程间的关系为单向关联,一名学生可能要上多门课程,课程是个抽象的东西他不拥有学生。


1.4 聚合(Aggregation)

【(由……)聚合关系,has-a】:是整体与部分的关系。如车和轮胎是整体和部分的关系。聚合关系是关联关系的一种,是强的关联关系;

关联和聚合在语法上无法区分,必须考察具体的逻辑关系。是一种特殊的关联关系,但是一般说关联关系是指这两个类在同一层次上,而聚合是 局部 指向 整体的关系。

【代码体现】:成员变量
【箭头及指向】:带空心菱形的实心线,菱形指向整体

这里写图片描述


1.5 组合(Composition)

【(由……)组合关系,be-composed-of】:是整体与部分的关系.,没有公司就不存在部门 组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。

注意,组合是不能共享的。即代表部分的对象在每一个时刻只能和一个代表整体的对象发生组合关系,并且由后者排他地负责代表部分的对象的生命周期。这是它和聚合关系的差别。

举例:
大学里,一个社团由一个个学生组成,所以可以说 学生 聚合 为社团,而一个学生可以同时参加多个社团,所以这是一种聚合关系,而不是组合关系。

【代码体现】:成员变量
【箭头及指向】:带实心菱形的实线,菱形指向整体

这里写图片描述


1.6 依赖(Dependency)

【依赖关系】:是一种使用的关系,所以要尽量不使用双向的互相依赖。

它与关联关系的区别在于,关联的耦合性更强。关联的代码表现往往是成员变量,而依赖关系的代码表现是局部变量和方法的参数这种较弱的关联。

【代码表现】:局部变量、方法的参数或者对静态方法的调用。
【箭头及指向】:带箭头的虚线,指向被使用者。

这里写图片描述


1.7 小结

各种关系的强弱顺序:泛化= 实现> 组合> 聚合> 关联> 依赖

下面这张UML图,比较形象地展示了各种类图关系:

这里写图片描述



2 设计原则

2.1 开闭原则OCP

(Open-Close Principle)开闭原则是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。1988年,勃兰特·梅耶(Bertrand Meyer)在他的著作《面向对象软件构造(Object Oriented Software Construction)》中提出了开闭原则,它的原文是这样:“Software entities should be open for extension,but closed for modification”。

实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。

我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。

关于系统可变的部分,还有一个更具体的对可变性封装原则(Principle of Encapsulation of Variation, EVP),它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。


2.2 里氏代换原则(LSP)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

【应用实例】:
LSP讲的是基类和子类的关系。只有当这种关系存在时,里氏代换关系才存在。如果两个具体的类A,B之间的关系违反了LSP的设计,(假设是从B到A的继承关系)那么根据具体的情况可以在下面的两种重构方案中选择一种。

—–创建一个新的抽象类C,作为两个具体类的超类,将A,B的共同行为移动到C中来解决问题。

—–从B到A的继承关系改为委派关系。

【详细解释】:
为了说明,我们先用第一种方法来看一个例子,第二种办法在另外一个原则中说明。我们就看那个著名的长方形和正方形的例子。对于长方形的类,如果它的长宽相等,那么它就是一个正方形,因此,长方形类的对象中有一些正方形的对象。对于一个正方形的类,它的方法有个setSide和getSide,它不是长方形的子类,和长方形也不会符合LSP。

  • 长方形
    public class Rectangle {
        ...
        setWidth(int width){
            this.width=width;
        }

        setHeight(int height){
            this.height=height
        }
    }
  • 正方形
    public class Square{
        ...
        setWidth(int width){
            this.width=width;
            this. height=width;
        }
        setHeight(int height){
            this.setWidth(height);
        }
    }
  • 改变边长的函数
    public void resize(Rectangle r) {
        while (r.getHeight() <= r.getWidth) {
            r.setHeight(r.getWidth + 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值