Delegate Class? What is it?
我们先来看问题:
有一个concrete class,它有两个职责R1和R2。按照SRP(Single Responsibility Principle),我们需要把这两个职责放到两个抽象类中,然后由它来继承/实现这两个抽象类。
在这两个职责类中,都存在着需要concrete class实现的抽象方法,以及自身的模板方法。如下:
public abstract class R1 {
public abstract void r1m1();
public abstract void r1m2();
public abstract void r1m3();
// template method
public void r1m() {
r1m1();
r1m2();
r1m3();
}
}
public abstract class R2 {
public abstract void r2m1();
public abstract void r2m2();
// template method
public void r2m() {
r2m1();
r2m2();
}
}
由于Java/C#等语言只允许单一继承,我们不得不把至少一个职责实现为接口。那么我们不妨把R2实现为接口。另外,由于这些语言不允许在接口中定义非抽象方法,我们不得不另作选择:
1.把模版方法也定义为抽象的,由实现类来实现它。
public interface R2 {
void r2m1();
void r2m2();
void r2m();
}
public class Foo extends R1 implements R2 {
...
public void r2m1() { ... }
public void r2m2() { ... }
public void r2m() {
r2m1();
r2m2();
}
}
这种方法看起来可以解决问题,但事实上是一种非常糟糕的方案,因为:
首先,接口R2并不仅仅被Foo实现,它还可能被其他的类实现。如果使用这种方法,将不得不把r2m的实现在每个实现类中拷贝一份。
更重要的是,从概念抽象而言,r2m并不是一个需要实现类实现的方法,它与那些抽象方法处于不同的层次和范畴。把r2m的实现放到Foo中,等于把两个层次的问题耦合到了一起。
2、把r2m从R2中拿出来,放到client中。
public interface R2 {
void r2m1();
void r2m2();
}
public class Foo extends R1 implements R2 {
...
public void r2m1() { ... }
public void r2m2() { ... }
}
public class Client {
private R2 serviceProvider;
public void doSomething() {
...
r2m();
...
}
public void r2m() {
serviceProvider.r2m1();
serviceProvider.r2m2();
}
...
}
这种方法就是典型的Strategy模式,Client拥有一个接口R2的实现类的实例,R2的不同实现者通过实现R2的抽象方法来提供不同strategy,而client则通过r2m中定义的算法来使用某种strategy。
问题在于,这种方法把client与r2m所提供的算法耦合到一起了。而事实上,r2m本来是独立于任何client的。如果用这种方法,当存在多个client的情况下,我们不得不把r2m中的算法在每个client中都要实现一遍。Bad smell, right?
3、把r2m放到一个中间类中。
class R2Algorithm {
private R2 serviceProvider;
public void r2m() {
serviceProvider.r2m1();
serviceProvider.r2m2();
}
public R2Algorithm(R2 sp) {
serviceProvider = sp;
}
}
public class Client {
private R2Algorithm r2;
public void doSomething() {
...
r2.r2m();
...
}
}
这种方法破除了接口,算法,以及client之间的耦合关系,让系统变得灵活和便于修改。但代价是,Client不得不创建一个额外的R2Algorithm对象。仔细观察一下就会得知,这个对象是根本没有必要存在的,因为它仅仅提供了算法。所以,我们可以:
4、把r2m放到一个工具类中。
class R2Algorithm{
public static void r2m(R2 sp) {
sp.r2m1();
sp.r2m2();
}
}
public class Client {
private R2 serviceProvider;
public void doSomething() {
...
R2Algorithm.r2m(ServiceProvider);
...
}
...
}
这是使用Java语言所能找到的最好的解决方案。
对于这种模式化的概念,我们可以把中间的工具类抽象为一个接口委托的概念。一个接口委托就是这样一个工具类,它本身委托了一个接口,然后通过访问接口方法来实现算法。它的特性为:
? 委托类只能委托自一个接口;
? 一个interface可以被任意多个委托类委托;
? 委托类本身不能被实例化;
? 委托类和它所委托的接口之间可以实现自动类型转化;
? 委托类内部仅仅可以定义static的变量;
? 委托类可以被继承,继承类仍然为父类所委托接口的委托类;
? 一个委托类不能继承自一个非委托类;
? Client通过委托类除了可以访问委托类的方法,还可以直接访问接口中的方法.
我们来看一个完整的例子:
public interface R2 {
void r2m1();
void r2m2();
}
public class Foo extends R1 implements R2 {
...
public void r2m1() { ... }
public void r2m2() { ... }
...
}
// 通过delegates关键字建立起委托关系
public class R2Algrithm delegates R2 {
public void r2m() {
r2m1();
r2m2();
}
}
public class Client {
// 注意这里,一个Foo对象可以自动转化为委托类R2Algorithm类型
private R2Algorithm serviceProvider = new Foo();
public void doSomething() {
...
serviceProvider.r2m1(); // 直接访问接口方法
serviceProvider.r2m(); // 访问委托中的算法
...
}
}
通过委托类,程序员可以更加直接无缝的使用针对接口的算法,并且鼓励设计师对系统进行更加良好的设计。
当前的语言并不直接支持这种概念,Dominoo will。
你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=650136