定義:
由一個工廠對象決定決定創建出哪一種產品類的實例
類型:
創建型,不屬於GOF23種設計模式
適用場景:
工廠類負責創建的 對象比較少;
應用層只知道傳入工廠類的參數,對於如何創建對象不關心。
優點:
只需要傳入一個正確的參數,就可以獲取你所需要的對象,而無需知道其創建細節。
【實現責任分割,提供參數創建對象,客戶端(應用層)甚至可以不用知道產品類類名】
缺點:
工廠類的職責相對過重,增加新的產品需要修改工廠類的判斷邏輯,違背開閉原則;
無法形成基於繼承的等級結構。
代碼舉例:
業務場景:某網站有各種類型的視頻
源碼:點擊查看源碼
代碼位置:com.ljm.design.pattern.creational.simplefactory
創建一個視頻類: Video,有一個生產視頻的抽象方法
public abstract class Video {
//視頻的屬性暫時忽略
//有一個產生視頻的方法,因爲有各類視頻,所以是一個抽象方法
public abstract void produce();
}
創建一個java視頻的實現類JavaVideo繼承Video,
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("生產java課程視頻");
}
}
一個Python視頻的實現類PythonVideo繼承Video
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("生產Python課程視頻");
}
}
編寫測試類Test,模擬應用層。
public class Test {
public static void main(String[] args) {
//現在需要一個java課程
父類引用指向子類對象,調用子類重寫的方法
現在的應用層是非常依賴於JavaVideo的
Video viedo = new JavaVideo();
viedo.produce();
}
}
需要一個java課程,父類引用指向子類實現,調用子類重寫的方法,現在的應用層是非常依賴於JavaVideo的。那麼能不能不讓應用層代碼依賴對應的類,把整個生產實例的過程放到一個類裏面,使應用層不依賴對應的具體實現類。這就是簡單工廠的由來
改進:是應用層不依賴於集體的某個實現類,而是通過一個工廠類,來實現生產視頻的過程。創建生產視頻的工廠類:VideoFactory:
public class VideoFactory {
/**
* 創建一個生產視頻的方法
* 根據type判斷要創建的課程
* v1.0
*/
public Video getVideo(String type){ //也可以定義爲靜態方法
if ("java".equalsIgnoreCase(type)){
return new JavaVideo();
} else if("Python".equalsIgnoreCase(type)){
return new PythonVideo();
} else {
return null;
}
}
}
使用工廠創建之前,查看一下類圖,等等好做比較:
註釋前面的測試代碼,重新編寫測試代碼,現在不是new課程出來,而使用剛剛寫的工廠類:
/**
* 工廠方式
* 不在依賴於某個具體課程
*/
VideoFactory factory = new VideoFactory();
Video video = factory.getVideo("java");
if (video == null){
return;
}
video.produce();
在查看一下最新的UML類圖:
應用層只依賴VideoFactory,具體的JavaVideo或者PythonVideo由VideoFactory根據參數負責創建
這種方式缺點:隨着要生產的課程種類越來越多,工廠裏的方法要不斷修改,不符合開閉原則,增加錯誤概率
改進: 利用反射彌補擴展性,重新定義VideoFactory內的getVideo 方法
/**
* 生產視頻的方法
* 利用反射原理
* @param c 要生產視頻的Class對象
* @return 生產出的課程
*/
public Video getVideo(Class c){
Video video = null;
try {
video = (Video) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return video;
}
註釋前面的測試代碼,重新編寫測試代碼:
/**
* 測試:反射方式創建需要的課程
*/
VideoFactory factory = new VideoFactory();
Video video = factory.getVideo(PythonVideo.class);
video.produce();
JDK(1.7)源碼分析:
java.util.Calendar 類下的getInstance方法。
/**
* Gets a calendar using the default time zone and locale. The
* <code>Calendar</code> returned is based on the current time
* in the default time zone with the default locale.
*
* @return a Calendar.
*/
public static Calendar getInstance()
{
Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
cal.sharedZone = true;
return cal;
}
進入 createCalendar方法:
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
Calendar cal = null;
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype == null) {
// Calendar type is not specified.
// If the specified locale is a Thai locale,
// returns a BuddhistCalendar instance.
//這裏和VideoFactory原來的寫法異曲同工的
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
} else if (caltype.equals("japanese")) {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else if (caltype.equals("buddhist")) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
// Unsupported calendar type.
// Use Gregorian calendar as a fallback.
cal = new GregorianCalendar(zone, aLocale);
}
return cal;
}
按住Shift+Ctrl+Alt+U查看類圖 ----> 按住Ctrl+Alt+B經他的子類都依次顯示出來:
這是學習一些源碼的方式
其他源碼
先在pom導入依賴,詳情見pom.xml
再來看一下logback裏面簡單工廠的使用,先打開LoggerFactory,找到裏面的重載方法getLogger
/**
* Return a logger named according to the name parameter using the statically
* bound {@link ILoggerFactory} instance.
*
* @param name The name of the logger.
* @return logger
*/
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
/**
* Return a logger named corresponding to the class passed as parameter, using
* the statically bound {@link ILoggerFactory} instance.
*
* @param clazz the returned logger will be named after clazz
* @return logger
*/
public static Logger getLogger(Class clazz) {
return getLogger(clazz.getName());
}
getLogger(Class clazz)也是調用的getLogger(String name)方法,getLogger(String name)中調用的是ILoggerFactory的getLogger方法,查看ILoggerFactory:
public interface ILoggerFactory {
public Logger getLogger(String name);
}
他是一個接口,下面是一個方法(這個是工廠方法,先跳過),回到getLogger方法,這裏LoggerFactory肯定有多個實現,選中getLogger方法,Ctrl+Alt+B查看他的實現,這裏選擇其中一個實現類LoggerContext,
//調用的下面的方法
public final Logger getLogger(final Class clazz) {
return getLogger(clazz.getName());
}
//傳入一個name,返回Logger
public final Logger getLogger(final String name) {
if (name == null) {
throw new IllegalArgumentException("name argument cannot be null");
}
//返回root節點(用於檢索根記錄器的名稱)的logger
if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
return root;
}
int i = 0;
Logger logger = root;
//檢查所需的記錄器是否存在,如果存在,則返回它
Logger childLogger = (Logger) loggerCache.get(name);
if (childLogger != null) {
return childLogger;
}
String childName;
while (true) {
int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
if (h == -1) {
childName = name;
} else {
childName = name.substring(0, h);
}
i = h + 1;
synchronized (logger) {
childLogger = logger.getChildByName(childName);
if (childLogger == null) {
childLogger = logger.createChildByName(childName);
loggerCache.put(childName, childLogger);
incSize();
}
}
logger = childLogger;
if (h == -1) {
return childLogger;
}
}
}
這裏也是根據傳入的參數name,選擇要返回的Logger