Java新手的跌倒日記 - 再看 Tell don't ask 及委派方法

 Wed, 30 Jan 2008 17:22:52 +0800

在之前的文章「Java新手的跌倒日記 - Tell Don't Ask?」裡面,搞不懂Nat Pryce所說的Tell don't ask的意思。這幾天看了一些委派方法以及composite pattern的文章,重新設計了一個產品/零件的例子,似乎問題就比較清楚了。

之前嘗試實作出Nat Pryce所說的不良示範,但是對於如何改良比較摸不著頭緒。後來想了一下,其實零件可以組合成產品,產品又可能是其他產品的零件,這是一種composite的實例,所以再寫幾個例子來測試一下:

package idv.fillano.part;
public interface Part {
public void setName(String name);
public String getName();
public void setCost(double cost);
public Double getCost();
}
Part.java

package idv.fillano.part;
public class PartImpl implements Part {
private Double cost=new Double(0);
private String name="";
public Double getCost() {
return cost;
}
public String getName() {
return name;
}
public void setCost(double cost) {
this.cost = cost;
}
public void setName(String name) {
this.name = name;
}
}
PartImpl.java

package idv.fillano.part;
import java.util.ArrayList;
public interface Product {
public int addPart(Part part);
public Part getPart(int index);
public void rmPart(int index);
public Double getTotalCost();
public Double getPartCost(String name);
public ArrayList getPartNames();
}
Product.java

package idv.fillano.part;
import java.util.ArrayList;
import java.util.Iterator;
public class ProductImpl extends PartImpl implements Product {
private ArrayList parts=null;
public ProductImpl() {
super();
parts = new ArrayList();
}
public int addPart(Part part) {
parts.add(part);
return parts.size();
}
public Part getPart(int index) {
return parts.get(index);
}
public Double getTotalCost() {
Double tmp = new Double(0);
Iterator it = parts.iterator();
while(it.hasNext()) {
Part pt=it.next();
tmp += pt.getCost();
}
tmp += getCost();
return tmp;
}
public void rmPart(int index) {
parts.remove(index);
}
public Double getPartCost(String name) {
Part tmp = getPartByName(name);
if (tmp==null) {
return null;
} else {
return tmp.getCost();
}
}
public Part getPartByName(String name) {
if (parts.size()==0) return null;
Iterator it = parts.iterator();
while(it.hasNext()) {
Part tmp = it.next();
if (tmp.getName()==name) return tmp;
if (tmp.getClass().getName()=="idv.fillano.part.ProductImpl") {
Part tmp1 = ((ProductImpl)tmp).getPartByName(name);
if (tmp1!=null) return tmp1;
}
}
return null;
}
public ArrayList getPartNames() {
ArrayList  ret = new ArrayList();
if (parts.size()==0) return ret;
Iterator it = parts.iterator();
while(it.hasNext()) {
Part tmp = it.next();
ret.add(tmp.getName());
if (tmp.getClass().getName()=="idv.fillano.part.ProductImpl") {
ArrayList  tmp1 = ((ProductImpl)tmp).getPartNames();
if (tmp1.size()>0) {
ret.addAll(tmp1);
}
}
}
return ret;
}
}
ProductImpl.java

然後試用一下:

package idv.fillano.part;
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
PartImpl part1 = new PartImpl();
PartImpl part2 = new PartImpl();
PartImpl part3 = new PartImpl();
PartImpl part4 = new PartImpl();
ProductImpl product1 = new ProductImpl();
ProductImpl product2 = new ProductImpl();
ProductImpl product3 = new ProductImpl();
part1.setName("part1");
part1.setCost(1.5);
part2.setName("part2");
part2.setCost(2.3);
part3.setName("part3");
part3.setCost(3.8);
part4.setName("part4");
part4.setCost(4.2);
product1.setName("product1");
product1.setCost(0.3);
product2.setName("product2");
product2.setCost(0.5);
product3.setName("product3");
product3.setCost(0.5);
product1.addPart(part1);
product1.addPart(part2);
product2.addPart(product1);
product2.addPart(part3);
product3.addPart(part4);
product3.addPart(product2);
ArrayListnames = product3.getPartNames();
System.out.println("Total cost of product3: "+product3.getTotalCost());
System.out.println("Total cost of product2: "+product2.getTotalCost());
System.out.println("Total cost of product1: "+product1.getTotalCost());
System.out.println("Get part cost from product3: "+product3.getPartCost("product1"));
System.out.println("Parts names of product3:");
Iterator it = names.iterator();
while(it.hasNext()) {
System.out.println("-> "+it.next());
}
}
}

跑出來的結果是:

Total cost of product3: 5.2
Total cost of product2: 4.6
Total cost of product1: 4.1
Get part cost from product3: 0.3
Parts names of product3:
-> part4
-> product2
-> product1
-> part1
-> part2
-> part3

在我之前的例子裡,SubPart是Part的一個field,這樣兩個的關係就耦合了。現在用ArrayList容器,讓parts field可以放Part的子類別,多少可以解耦一點。放到parts的Part或Product是透過addPart方法放進來的,針對parts容器內物件的操作,會更像委派。

另外,使用Product的getTotalCost()可以計算所有sub parts加總的成本;使用getPartCost(part's name)方法,可以取得sub part的成本;利用getPartNames()可以取得所有sub parts的名字。

不知道我這樣做,是否就是Nat Pryce說的意思呢...

(倉促寫的例子,不是很嚴謹就是了。)