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(); }
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; } }
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 ArrayListgetPartNames(); }
package idv.fillano.part; import java.util.ArrayList; import java.util.Iterator; public class ProductImpl extends PartImpl implements Product { private ArrayListparts=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; } }
然後試用一下:
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說的意思呢...
(倉促寫的例子,不是很嚴謹就是了。)