之前有稍微對Design Pattern做了一些描述,大概說明了一下什麼是design pattern。今天會講解一下其中的一種:factroy methody...
factory method
主要是當需要產生複雜的物件的時候使用。 由於產生物件的過程複雜,封裝起來,讓使用物件的人不需要知道產生的過成,就可以取得這個物件。
沒什麼用的Hello World...
比方說我有一個書櫃
的類別(BookShelf
)。有書櫃就有書
,不過書並不是我們這次要講的重點,所以我們跳過… 。
回到書櫃這個類別。
書櫃的建構過程如下:
... 前面還有50行程式碼...
List<Book> books = bookDao.findBookBy(shelfId);
BookVo[] bookVos = new BookVo[books.size()];
... 假設這裡有100行程式碼...
BookShelf bookShelf = new BookShelf(bookVos);
Shelf shelf = shelfDao.findShelf(shelfId);
bookShelf.setShelf(shelf);
於是我用一個function(在Java 稱為method
)將這麼多程式碼封裝起來,…
public class ShelfFactory {
public BookShelf getBookShelf(String shelfId) {
...前面的150行程式碼...
return bookShelf;
}
}
於是當有人要取得BookShelf
物件時,如果直接用new BookShelf()
是行不通的,而且為了達到更好的封裝效果,建構子只有在相同一個package
才能呼叫。 用戶端如果要取得BookShelf
物件,只要使用可以很快速取得的ShelfFactory,就可以取得很裡雜的BookShelf
物件…
... 前面只要2行可以取得context物件...
ShelfFactory boFactory = context.getBean(ShelfFactory.class);
BookShelf bookShelf = boFactory.getBookShelf("POMO");
眼尖的讀者,可能開始在猜context
物件不會是Spring framework裡面的東西吧? 沒錯,就是你所想的那種東西。
通常來說Factory的function,都會使用static
的宣告,因為Class
是可以直接取得的。
很常用的factory method pattern
前面的hello world用處不太大。 不過還記得我在之前的文章裡介紹,pattern 就是一再重複的東西,其實你時常在用,只是沒注意到。我舉一些例子…
Calendar calendar = Calendar.getInstance(); //取得Calendar 物件
Logger logger = LoggerFactory.getLogger(getClass()); //取得logger物件
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext); //取得Spring context物件
這幾個物件,大家應該每天都在用,尤其是logger。
如果你有用過Spring 那你一定有用過
還有如果你有使用過Spring famework,你就一定有用過的一個factory method。 還記得上面那個看似沒用的hello world 裡,利用spring framework取得ShelfFactory
物件的部份嗎? 也是使用simple facotry pattern喔。
ShelfFactory boFactory = context.getBean(ShelfFactory.class); //其實spring 產生bean的過程是很裡複雜的
如果去trace Spring 的getBean
跑最後,大概是這樣的一段,你說建構這樣一個bean會不會很複雜…
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length > 1) {
ArrayList<String> autowireCandidates = new ArrayList<String>();
for (String beanName : beanNames) {
if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
autowireCandidates.add(beanName);
}
}
if (autowireCandidates.size() > 0) {
beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
}
}
if (beanNames.length == 1) {
return getBean(beanNames[0], requiredType, args);
}
else if (beanNames.length > 1) {
Map<String, Object> candidates = new HashMap<String, Object>();
for (String beanName : beanNames) {
candidates.put(beanName, getBean(beanName, requiredType, args));
}
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return getBean(primaryCandidate, requiredType, args);
}
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return getBean(priorityCandidate, requiredType, args);
}
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
}
else if (getParentBeanFactory() != null) {
return getParentBeanFactory().getBean(requiredType, args);
}
else {
throw new NoSuchBeanDefinitionException(requiredType);
}
}
getBean
的宣告是:public getBean
,需要傳入requiredType,指定回傳型別,以及一些其他所需要的參數…什麼是封裝
小時候在學物件導向時,背知道物件導向的三大特性:封裝、繼承、多型。 在講解封裝時沒什麼用的HelloWorld大概是這樣:
public BigDecimal getSalary() {
return salary;
}
那時總沒什麼感覺,總在納悶為什麼這樣子的封裝會引起世界的潮流。 想來是那時真沒什麼實務經驗。 實務上封裝的應用,會是像上頭舉的Spring framework裡的getBean
這樣。有了封裝,我們只要呼叫getBean
這一個method,丟入所需要參數即可,不用花時間去了解這麼複雜的建構過程。
搭配應用
Singleton
Factory method通常會搭配Singleton一起應用。 上面的例子其實有搭配了singleton的1/4招─一半私有化(private
)建構子─而且還不是完全私有化,因為使用的是package
層級。Template method
之前的例子,是回傳BookShelf
物件,但我可以回傳Shelf
物件。使用的人可以不用在意,取到的實例是BookShelf
或是PromtionShelf
。
結語
Factory method是很簡單的一種pattern,他也可以算是其他應用的基礎。 如果以武功招式來說,他可以算是起手式。 大家要好好熟悉呀!
更新
2015-05-02: 將原名稱為simple factory改為factory method.
沒有留言 :
張貼留言