參考資料
圖解設計模式
大話設計模式
設計模式之禪
github我見過最好的設計模式
設計原則回顧
| 設計原則 | 解釋 |
|---|---|
| 開閉原則 | 對擴展開放,對修改關閉 |
| 依賴倒置原則 | 通過抽象讓哥哥模塊互不影響,松耦合,面向介面編程 |
| 單一職責原則 | 一個介面,類,方法只做一件事 |
| 介面隔離原則 | 保證純潔性,不應該依賴于自己不需要的介面,有時候沒辦法可以通過配接器來解決 |
| 迪米特法則 | 最少知道原則,一個類對其所依賴的類知道的越少越好 |
| 里氏替換原則 | 子類可以擴展父類的功能但是不能改變父類原有的功能 |
| 合成復用原則 | 盡量使用聚合和組合,少用繼承 |
- 巧用Json工具類來做json轉化
- JDBC這一塊是固定的,可以使用模板方法
工廠模式
簡單工廠
案例
日歷類
比如說日歷類中獲取對應日歷的方法,通過傳入引數來進行對應的物件的生成

日志
又或者說 logfactory中的log


public class CourseFactory {
// public ICourse create(String name){
// if("java".equals(name)){
// return new JavaCourse();
// }else if("python".equals(name)){
// return new PythonCourse();
// }else {
// return null;
// }
// }
// public ICourse create(String className){
// try {
// if (!(null == className || "".equals(className))) {
// return (ICourse) Class.forName(className).newInstance();
// }
//
// }catch (Exception e){
// e.printStackTrace();
// }
// return null;
// }
//泛型約束
public ICourse create(Class<? extends ICourse> clazz){
try {
if (null != clazz) {
return clazz.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
Mybatis SqlsesssionFactory創建Executor
org.apache.ibatis.session.Configuration#newExecutor(Transaction, ExecutorType)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
- 通過不同的ExecurtorType來創建Executor
優點
只需要傳入一個正確的引數,就可以獲取我們所需要的物件,無需知道其創建的細節
缺點
工廠類的職責相對過重,增加新的產品時需要修改工廠類的判斷邏輯,不符合開閉原則
問題
為什么一定要用工廠模式,為什么工廠模式比直接
new Object()好呢?
- new Object可能還得去配置和組裝物件的很多額外資訊
- 但是當前可能并不需要知道這么多額外資訊,且不希望自己來組裝,所以通過工廠來獲取
- 例如用戶只想要一輛車,可能并不想知道車門的材料是什么廠商的,車輪是從哪里進口的
- 其次如果說工廠里面生產的東西發生一些小的變更,只需要直接在工廠的地方修改就行了,如果用戶自己來new的,那么所有用戶自己創建的地方都需要修改,其實主要還是
高內聚低耦合
工廠方法
定義一個創建物件的介面,但讓實作這個介面的類來決定實體化哪個類,工廠方法讓類的實體化推遲到子類中進行,
- 缺點
- 類的個數容易過多,增加了代碼結構的復雜度
- 增加了系統的抽象性和理解難度
例子
如果是簡單當前可能創建不同的工廠產品可能需要組裝各種屬性,可能從java上來說就是需要set各種各樣的東西,就會導致代碼很長,而且一旦后期需要維護和變更其中一個工廠,可能會改和影響到其他工廠,所以這時候為了開閉原則,我們可以把工廠抽象出來,對應的工廠實作自己的工廠創建,
關于課程的例子

工廠抽象
public interface ICourseFactory {
ICourse create();
}
課程抽象
public interface ICourse {
/**
* 錄制視頻
* @return
*/
void record();
}
關于女媧造人的例子

抽象方法中已經不再需要傳遞相關引數了,因為每一個具體的工廠都已經非常明確自己的職責:創建自己負責的產品類物件,
工廠方法+單例模式
單例模式代碼
public class Singleton {
//不允許通過new產生一個物件
private Singleton(){ }
public void doSomething(){
//業務處理
}
}
工廠方法+反射破壞構造私有化來創建
我們可以通過工廠來構建單例物件
public class SingletonFactory {
private static Singleton singleton;
static {
try {
Class cl = Class.forName(Singleton.class.getName());
//獲得無參構造、
java.lang.reflect.Constructor constructor =cl.getDeclaredConstructor();
//設定無參構造是可訪問的
constructor.setAccessible(true);
//產生一個實體物件
singleton = (Singleton)constructor.newInstance();
} catch (Exception e) {
//例外處理
}
}
public static Singleton getSingleton(){ return singleton; }
}
通過獲得類構造器,然后設定訪問權限,生成一個物件,然后提供外部訪問,保證記憶體中的物件唯一,
當然,其他類也可以通過反射的方式建立一個單例物件,確實如此,
但是一個專案或團隊是有章程和規范的,何況已經提供了一個獲得單例物件的方法,為什么還要重新創建一 個新物件呢?
除非是有人作惡,
Spring中使用工廠方法+反射破壞構造私有化
org.springframework.core.io.support.SpringFactoriesLoader#instantiateFactory
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
try {
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
throw new IllegalArgumentException(
"Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
}
Constructor<?> constructor = instanceClass.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
return (T) constructor.newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex);
}
}
工廠方法+單例延遲初始化

public class ProductFactory {
private static final Map<String,Product> prMap = new HashMap();
public static synchronized Product createProduct(String type) {
Product product =null;
//如果Map中已經有這個物件
if(prMap.containsKey(type)){
product = prMap.get(type);
}else{
if(type.equals("Product1")){
product = new ConcreteProduct1();
}else{
product = new ConcreteProduct2();
}
//同時把物件放到快取容器中
prMap.put(type,product);
} return product;
}
}
Dubbo中SPI工廠方法+單例
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
代碼還比較簡單,通過定義一個Map容器,容納所有產生的物件, 如果在Map容器中已經有的物件,則直接取出回傳;如果沒有,則根據 需要的型別產生一個物件并放入到Map容器中,以方便下次呼叫,
延遲加載框架是可以擴展的,例如限制某一個產品類的最大實體化 數量,可以通過判斷Map中已有的物件數量來實作,這樣的處理是非常 有意義的,例如JDBC連接資料庫,都會要求設定一個MaxConnections 最大連接數量,該數量就是記憶體中最大實體化的數量,
延遲加載還可以用在物件初始化比較復雜的情況下,例如硬體訪 問,涉及多方面的互動,則可以通過延遲加載降低物件的產生和銷毀帶 來的復雜性,
創建物件需要大量重復的代碼
客戶端不依賴于產品類實體如何被創建,實作等細節,一個類通過其子類來指定創建哪個物件
最佳實踐
工廠方法模式在專案中使用得非常頻繁,以至于很多代碼中都包含工廠方法模式,該模式幾乎盡人皆知,但不是每個人都能用得好,熟能生巧,熟練掌握該模式,多思考工廠方法如何應用,而且工廠方法模式還可以與其他模式混合使用(例如模板方法模式、單例模式、原型模式等),變化出無窮的優秀設計,這也正是軟體設計和開發的樂趣所在,
缺點
- 類的個數容易過多,增加復雜度
- 增加了系統的抽象性和理解難度
問題
工廠方法它解決了簡單工廠的哪個問題?
- 解決了不同工廠生成不同產品需要裝配過多的代碼在同一個類里面,違背了開閉原則,通過工廠方法來解決快速擴展的問題
抽象工廠
客戶端無需了解其所呼叫工廠的具體類資訊,
圖解抽象工廠
提供一個創建一系列相關或相互依賴物件的介面,無須指定他們具體的類
客戶端不依賴于產品類實體如何被創建、實作等細節
強調一系列相關的產品物件(屬于同一產品族)一起使用創建物件需要大量重復的代碼

- 產品族
- 代表同一個品牌,不同產品
- 產品等級結構
- 同一產品不同品牌
例子

public class AbstractFactoryTest {
public static void main(String[] args) {
JavaCourseFactory factory = new JavaCourseFactory();
factory.createNote().edit();
factory.createVideo().record();
}
}
通用設計

我們可以通過指定不同的抽象工廠來創建對應的產品
適用場景
- 如果代碼需要與多個不同系列的相關產品互動, 但是由于無法提前獲取相關資訊, 或者出于對未來擴展性的考慮, 你不希望代碼基于產品的具體類進行構建, 在這種情況下, 你可以使用抽象工廠,
- 抽象工廠為你提供了一個介面, 可用于創建每個系列產品的物件, 只要代碼通過該介面創建物件, 那么你就不會生成與應用程式已生成的產品型別不一致的產品,
- 如果你有一個基于一組抽象方法的類, 且其主要功能因此變得不明確, 那么在這種情況下可以考慮使用抽象工廠模式,
- 在設計良好的程式中, 每個類僅負責一件事, 如果一個類與多種型別產品互動, 就可以考慮將工廠方法抽取到獨立的工廠類或具備完整功能的抽象工廠類中,
優缺點
| 優點 | 缺點 |
|---|---|
| 你可以確保同一工廠生成的產品相互匹配, | 由于采用該模式需要向應用中引入眾多介面和類, 代碼可能會比之前更加復雜, |
| 你可以避免客戶端和具體產品代碼的耦合, | |
| 單一職責原則, 你可以將產品生成代碼抽取到同一位置, 使得代碼易于維護, | |
| 開閉原則, 向應用程式中引入新產品變體時, 你無需修改客戶端代碼, |
- 不符合開閉原則
- 如果在對應的工廠上面新增一個功能,所有實作的地方都需要去修改
慎用
- 如果在對應的工廠上面新增一個功能,所有實作的地方都需要去修改
- 不依賴于產品實體如何被創建,實作的細節
- 強調一系列相關的產品(統一產品族)一起使用穿件物件需要大量重復的的重復代碼的場景
- 擴展性很強
問題
舉例說出抽象工廠的應用場景,我如果要增加一個答疑的產品應該怎么修改代碼
- 比如說女媧造人,需要有男人和女人,也需要有白種人,黑種人,黃種人,那么我們可以定義一個頂層介面
Humanfactory然后FemaleFactory和MaleFactory都實作對應的HumanFactory的介面,然后對應的工廠創建對應的產品- 增加答疑的產品的話需要新增一個答疑的介面,所有工廠都需要去修改補充對應的實作,所以改動會非常大
我的筆記倉庫地址gitee 快來給我點個Star吧
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/242679.html
標籤:Java
下一篇:Log4j日志框架

