马上要开始写实验三了,其中要用到委托机制。老师上课讲的太快了,没怎么记住,今天复习整理一下委托机制。
委托其实就是一个对象请求另一个对象的功能,与它相对的是继承,implements。继承的好处是子类可以直接使用父类的所有方法,但问题是如果我们的子类不需要父类的一些方法需要override为空方法或用其他方式来禁用。而委托在这方面就很安全,用户无法直接得到我们委托给的那个对象。
其实我在实验2中就已经使用了委托机制,比如GraphPoet类中定义了一个Graph类的变量来使用它的一些方法,FriendShipGraph中也是一样。
下面通过讨论两种实现排序的方式来加强理解。注意使用SortedMap和SortedSet时也需要类似的方法。
第一种:实现Comparator接口
Comparator接口里声明的compare函数默认是两个int参数,如果我们不需要修改它的参数类型,即我们的比较器比较的也是两个int时,我们可以通过如下方式方便的定义我们的比较器:
final Comparator ASCENDING = (i,j) -> i<j;
然后这个比较器就可以被传给sort之类的函数了:
Collections.sort(list,ASCENDING);
但如果比价器比较的两个对象是我们自己定义的类,我们就可以写一个Comparator的实现然后override compare()函数:
public class Edge{
Vertex s,t;
double weight;
...
}
public class EdgeComparator implements Comparator<Edge>{
@Override int compare(Edge o1,Edge o2){
if(o1.getWeight()>o2.getWeight())
return 1;
else if(..==..)return 0;
else return -1;
}
}
public void sort(List<Edge> edges){
Comparator comparator=new EdgeComparator();
Collections.sort(edges,comparator);
}
这种方法就是用了委托的机制,即比较两个Edge类的对象时委托一个EdgeComparator对象来做。
第二种方法:使用继承的机制。让Edge实现Comparable接口,然后override(这里讲义上写的是override,但讲义代码里并没写override标签,我感觉父类里定义的参数应该不是Edge类,所以这里应该是重载而不是重写) compareTo()方法。这样不需要构建新的Comparator类,比较代码放在ADT内部。
public class Edge implements Comparable<Edge>{
Vertex s,t;
double weight;
...
public int compareTo(Edge o){
if(this.getWeight()>o.getWeight())
return 1;
else if(..==..) return 0;
else return -1;
}
}
public void sort(List<Edge> edges){
Collections.sort(edges);
}
委托的类型:
下面设A类委托B类进行说明
1.Dependency:临时性的委托
A类的fields里没有B类,只是A类的某些方法的参数或内部使用了B类。
2.Association:永久性的委托
A类的fields里有B类。它有两种具体形态Composition和Aggregation。Composition是A类的构造函数不需要传入B类的参数,即A类对它委托的B类有默认设置,直接写在构造函数里了;Aggregation是A类的构造函数要传入一个B类的参数,代表A类委托这个特定的B类去做事。
实验三用到的CRP原则(Composite reuse principle):委托优先于继承
实验三的局部共性有三个维度,构建继承树的实现模式如下:
1.首先是需要定义三个维度,每个维度两种可能共6个接口。
2.针对某一维度的两个接口实现具体类
3.第二个维度分别拓展第一个维度的两个具体类,第二个维度实现4个具体类。
4.第三个维度拓展第二个维度的4个具体类,实现8个具体类。
我们会发现上述方案的复用性并不够好,当然它好于只为维度定义接口然后在各个子类中实现具体操作(这样每个维度的两种实现代码都要在剩下两个维度的4种组合中实现一次),但是只是继承树第一层的方法代码只需写一次,第二层的就要写2次了,第3层就要写4次,仍然是指数级的,设维度为m,我们要写的代码总数只是由
2
m
−
1
∗
(
m
∗
2
)
2^{m-1}*(m*2)
2m−1∗(m∗2)
变成了
2
+
4
+
.
.
.
+
2
m
=
2
m
+
1
−
2
2+4+...+2^{m}=2^{m+1}-2
2+4+...+2m=2m+1−2
仍然是指数级的。
而使用委托机制步骤如下:
1.同上面的1,定义6个接口
2.每个接口写一个实现类,共6个实现类
3.对每个具体任务写一个接口,它是上面6个接口的一种组合。
4.对每个具体任务的接口写实现类,实现类里面委托2里的实现类。
注意局部共性代码的实现是放在2中的,4中只是进行了委托,所以按上面的算法我们写的代码总数是
2
m
2m
2m的,非常优秀。