This page looks plain and unstyled because you're using a non-standard compliant browser. To see it in its best form, please visit upgrade to a browser that supports web standards. It's free and painless.

Fillano's Learning Notes 會員登入 會員註冊

最近在MockObjects網站上看到的文章:
Mock Objects: Tell Don't Ask and Mock Objects

簡單地說,就是讓物件在做判斷決策時,只倚賴內部的資訊或是透過參數傳進來的資訊,而不倚賴其他物件所有的資訊。透過這樣的設計,可以讓物件之間的耦合降低,同時也更容易做單元測試。

作者舉了一個極端的反例:像 "object.getPart().getSubpart().getAttribute()" 這樣的敘述如果在程式中出現的話,因為耦合了隔鄰的物件,同時又耦合了隔鄰的隔鄰物件,結果就很難用Mock Object來進行單元測試了。

解決的辦法,是透過refactoring重新設計....不知道怎麼做耶???作者說在隔鄰的物件新增方法???

我嘗試了一下,應該可以透過Eclipse的「產生委派方法」來實現???不是很確定,不過動手試了一下:

package com.fillano;

public class SubPart {
	private String attrib;

	public String getAttrib() {
		return attrib;
	}

	public void setAttrib(String attrib) {
		this.attrib = attrib;
	}
}
SubPart.java

package com.fillano;

public class Part {
	private SubPart subPart;

	public Part () {
		subPart = new SubPart();
	}

	public SubPart getSubPart() {
		return subPart;
	}

	public void setSubPart(SubPart subPart) {
		this.subPart = subPart;
	}
}
Part.java

package com.fillano;

public class Product {
	private Part part;

	public Product () {
		part = new Part();
	}

	public Part getPart() {
		return part;
	}

	public void setPart(Part part) {
		this.part = part;
	}
}
Product.java

這樣,就可以重現作者描述的train-wreck:

	Product pd = new Product();
	pd.getPart().getSubPart().setAttrib("an attrib");
	System.out.println(pd.getPart().getSubPart.getAttrib());

透過在Product與Part類別新增委派方法,可以讓Product直接get/set SubPart的attrib:

package com.fillano;

public class Part {
	private SubPart subPart;

	public Part() {
		subPart = new SubPart();
	}

	public String getAttrib() {
		return subPart.getAttrib();
	}

	public void setAttrib(String attrib) {
		subPart.setAttrib(attrib);
	}

	public SubPart getSubPart() {
		return subPart;
	}

	public void setSubPart(SubPart subPart) {
		this.subPart = subPart;
	}
}
新的Part類別

接著可以在Product類別中加入更多委派方法:

package com.fillano;

public class Product {
	private Part part;

	public Product() {
		part = new Part();
	}

	public String getAttrib() {
		return part.getAttrib();
	}

	public SubPart getSubPart() {
		return part.getSubPart();
	}

	public void setAttrib(String attrib) {
		part.setAttrib(attrib);
	}

	public void setSubPart(SubPart subPart) {
		part.setSubPart(subPart);
	}

	public Part getPart() {
		return part;
	}

	public void setPart(Part part) {
		this.part = part;
	}
}
新的Product類別

這樣似乎透過委派方法就可以去掉train-wreck的程式碼:

	Product pd = new Product();
	pd.setAttrib("an attrib");
	System.out.println(pd.getAttrib());

說實話,這並不是refactoring,我也沒有改進物件之間的耦合。想了一下,有一個原因是設計太簡單,其實無法反映出tell don't ask style的精神。


2008-1-25 更新

根據在java world看到的文章,我這樣做並不是委派方法,而是request forwarding與composition。他認為真正的委派方法中,物件會把自己當作委派方法的參數傳遞給執行委派的物件。


2008-1-30 更新

請看續集:Java新手的跌倒日記 - 再看 Tell don't ask 及委派方法,我把問題重新想了一下....