\[2019春软件构造\]面向可复用的软件构造

设计可复用的类

Liskov替换原则(LSP)

Let q(x) be a property provable about objects x of type T, then q(y) should be provable for objects y of type S where S is a subtype of T.

– Barbara Liskov

1
2
3
Animal a = new Animal();
Animal c1 = new Cat();
Cat c2 = new Cat();

在可以使用a的场景,都可以用c1和c2代替而不会有任何问题。

Liskov替换原则是衍生子类需要遵循的重要原则,它使得客户端可用统一的方式处理不同类型的对象,LSP要求子类相对于父类具有:

  • 更强的不变量
  • 更弱的前置条件
  • 更强的后置条件

注:更强的后置条件要求子类型中不能产生新的异常。

父类与子类的衔接类似于一个漏斗。

委派(Delegation)与组合(Composition)

A simple Delegation example

B类为对A类的一个委派,在B类中的私有字段a绑定了一个A类,B类的foo操作被委派给绑定的a来做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A {
void foo() {
this.bar();
}
void bar() {
print("a.bar");
}
}
class B {
private A a; // delegation link
public B(A a) {
this.a = a;
}
void foo() {
a.foo(); // call foo() on the a-instance
}
void bar() {
print("b.bar");
}
}

建立绑定关系:

1
2
A a = new A();
B b = new B(a); // establish delegation between two objects

Delegation vs Inheritance

From GeeksforGeeks

Delegation is simply passing a duty off to someone/something else.

  • Delegation can be an alternative to inheritance.
  • Delegation means that you use an object of another class as an instance variable, and forward messages to the instance.
  • It is better than inheritance for many cases because it makes you to think about each message you forward, because the instance is of a known class, rather than a new class, and because it doesn’t force you to accept all the methods of the super class: you can provide only the methods that really make sense.
  • Delegation can be viewed as a relationship between objects where one object forwards certain method calls to another object, called its delegate.
  • The primary advantage of delegation is run-time flexibility – the delegate can easily be changed at run-time. But unlike inheritance, delegation is not directly supported by most popular object-oriented languages, and it doesn’t facilitate dynamic polymorphism.

Delegation可以作为继承的一个替代品,它最主要的优点是灵活。相比于继承,它不需要继承另一个类的所有方法,而是可以通过委派机制调用部分方法;同时它支持类的动态绑定

Delegation发生在object层面,Inheritance发生在class层面。

几类常见Delegation

Dependency: 临时性的delegation

Dependency在调用函数时传入delegation类,并将操作委派给传入的类处理,是一种临时的delegation关系。

类与被委派的类之间的关系为uses-a

1
2
3
Flyable f = new FlyWithWings();
Duck d = new Duck();
d.fly(f);

Duck类的定义如下:

1
2
3
4
5
class Duck {
void fly(Flyable f) {
f.fly();
}
}

Association: 永久性的delegation

Association是一种永久的delegation关系,delegation关系通过将被委派的类存入字段的方式来实现。

类与被委派的类之间的关系为has-a

1
2
3
4
5
Flyable f = new FlyWithWings();
Duck d = new Duck(f);
d.fly();
d.setFlyBehavior(new CannotFly());
d.fly();

Duck类的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
class Duck {
Flyable f;
void Duck(Flyable f) {
this.f = f;
}
void setFlyBehavior(Flyable f) {
this.f = f;
}
void fly() {
f.fly();
}
}

Composition: 更强的association

Composition与Association类似,均将delegation关系存入字段。但不同之处在于Composition将委派关系的绑定写入了代码,在运行时委派关系不可变。

被委派的类与类之间的关系为is_part_of

1
2
Duck d = new Duck();
d.fly();

Duck类的定义如下:

1
2
3
4
5
6
class Duck {
Flyable f = new FlyWithWings();
void fly() {
f.fly();
}
}

Aggregation: 更弱的 association

Aggregation与Composition类似,均将delegation关系存入字段。与Composition的不同之处在于委派关系在运行时可变。

Aggregation与Composition还具有以下不同:Aggregation关系中的两个类不具有相互依存关系,而Composition中的两个类具有依附关系,即如果A has-a B,那么B不能脱离A存在。