設想,Apple和Grape都有相同的say()方法,但由於它們並沒有共同的商業邏輯,這時該怎么辦?
設想,要在SinaWeibo和TencentWeibo之間打通數據的獲取方法,如getId()、getNickName()等方法,但它們開放的第三方API接口並不一致,這時該怎么辦?如何提供出通用方法供團隊協作使用?
對於第一個設想,想解決問題,就要找到它們目前的共通點,它們都是水果,所以抽象出一個水果接口,利用工廠類生產水果。
對於第二個設想,想解決問題,同樣,要建立一個中間“工廠”,它們都實現這個工廠提供的接口,邏輯不就能統一了嗎。(想一下接口的作用就知道了。)
貼出“靜態工廠模式”的定義和理解:<引用自:簡單工廠模式(靜態工廠模式)深入理解 完全剖析>
靜態工廠模式:是類的創建模式,又叫做簡單方法模式,靜態產品模式是一個工廠對象決定創建出哪一種產品類的實例。
我們為什么要使用靜態工廠模式呢,它的有優點又是什么呢?
靜態工廠模式的優點:使用靜態工廠模式的優點是實現責任的分割,該模式的核心是工廠類,工廠類含有必要的選擇邏輯,可以決定什么時候創建哪一個產品的實例,而客戶端則免去直接創建產品的責任,而僅僅是消費產品。也就是說靜態工廠模式在不改變客戶端代碼的情況可以動態的增加產品。(總有人對靜態工廠模式增加產品后客戶端代碼不改變存有疑問,這里指的不改變,是指原來的消費產品的代碼不必改變,您如果是要消費新的產品當然得把新的產品加上啊),也就在某種程度上滿足開閉原則,但它並不完全滿足開閉原則,因為沒當增加新的產品時,都需要修改工廠類,所以出現了以后的工廠方法模式。
//假設所有的水果都具有一個say方法,所以我們抽象出一個水果的接口
- package com.xiang.staticfactory;
- interface Friut {
- public void say();
- }
//下面的蘋果類和葡萄類都實現這個水果接口
- class Apple implements Friut {
- public void say() {
- System.out.println("I am an Apple");
- }
- }
- class Grape implements Friut {
- public void say() {
- System.out.println("I am a Grape");
- }
- }
//下面我們就開始創建工廠類了,為什么要使用工廠類呢? 因為我們要使直接的程序盡量的滿足開閉原則。根據java的反射機制我們根據客戶端傳過來的字符串來動態的實例化客戶端消費的水果。試想,如果不這樣,那么該怎么去完成通過名字就得到類實例的功能呢?(注意:靜態工廠類本身不是靜態的,它里面的方法是靜態的,方法返回的要是實現了通用接口的對象的實例強轉過來的通用接口對象)
- class FriutFactory {
- public static Friut getFriut(String type) {
- Friut f = null;
- try {
- f = (Friut) Class.forName("com.xiang.staticfactory." + type)
- .newInstance();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return f;
- }
- }
//靜態工廠的運用:(只要根據一個傳過來的參數(水果名)就可以輕松地獲取它們對應的實例,從而調用它們自己的say()方法)。
- public class StaticFactory {
- public static void main(String[] args) {
- //假設傳過去的是Apple
- Friut f = FriutFactory.getFriut("Apple");
- if(f!=null){
- f.say();
- }else{
- System.out.println("暫時不生產此種水果");
- }
- }
- }
總結得到特定水果的過程:
1.抽象出通用接口,這里是Fruit接口(都是水果)
2.對象實現通用接口,這里是Apple和Grape實現Fruit接口
3.創建靜態工廠,通過特定參數組,得到通用接口對象
(當然也可以把Fruit接口直接就放在這個工廠里,同樣也是再完成第2步)
4.已經得到了通用接口對象,當然就可以直接調用完成同一種功能的同名方法了,這里是得到水果的名稱
為什么工廠類中的方法必須是靜態的?
下面咱們就來探討一下 ,其實這樣不用靜態的在語法上並沒有錯誤,但卻完全沒理解java面向對象設計的精髓。
如果直接new出FriutFactory的對象,客戶端就必須自己控制工廠類的構造和生成,客戶端必須非常清楚工廠的構造函數(比如構造函數有多少個參數,輸入參數時有什么條件等等),也知道工廠的內部細節,一旦工廠擴展或者改變了,客戶端在沒有得到通知的情況下就不知道怎么調用了。而是用靜態方法構造客戶端則完全不關心你是如何構造對象的所以客戶端不需要了解工廠的構造細節,一旦工廠內部發生變化,客戶端也不需要關心。它只需要關心最后的結果就行了,因為所有的細節都在工廠內部被處理完了。
下面來看一個靜態工廠:
- class CommonFactory{
- public static enum CommonType{
- A,B
- }
- public static CommonManager getManager(CommonType type){
- switch(type){
- case A:
- return AManager.getInstance();
- case B:
- return BManager.getInstance();
- default:
- throw new CommonException(CommonException.ERR_TYPE);
- }
- }
- public static interface CommonManager{
- public String method1(arg1);//爬山
- public void method2(CommonArg arg1,arg2);//打球
- }
- public static interface CommonArg{
- public void method3(arg1,arg2,arg3);
- }
- }
相信,大家對這個工廠所反映出的AManager、BManager兩個類也有了一定的理解,理解要是正反兩面的。
從上面的工廠可以推出,AManager、BManager肯定都有返回CommonManager類型的getInstance()靜態方法,肯定都實現了CommonManager接口從而完成爬山和打球的功能,即要@Override這兩個方法。其中,method2方法中有一個參數是下面的CommonArg接口對象類型,故method2方法中arg1只要為CommonArg類型即可。可以在AManager的構造器中初始化一些對象,然后還可以用到代理去完成CommonArg類型參數的適配。如,在AManager類中:
- @Override
- public void method2(CommonArg arg1,arg2){
- aMethod(new AOtherClass(arg1),arg2);
- }
- private class AOtherClass implements AMustInterface{
- private CommonArg arg1;
- private AOtherClass(CommonArg ca){
- arg1=ca;
- }
- ...
- arg1.method()...;
- }
對於上面代碼,最好結合它們的命名來理解意思,有很多內容通過命名可以看出來。上面aMethod方法要調用兩個參數,arg2好說,但arg1又不能直接調用,怎么辦?這里用所謂的代理來完成此類功能。aMethod方法的第一個參數必須是實現了MustInterface接口的對象,故就在實現這個接口的同時初始並實例化arg1。同樣,在BManager類中對應的BOtherClass也有它必須實現的接口BMustInterface。它們對於method2方法的實現都完成得很完美,因為都完成了“打球”功能,都完成了傳入兩個對應類型的參數從而完成某一項功能的目的。