簡單工廠模式
簡單工廠模式是指由一個工廠對象決定創建哪一種產品類的實例。
以課程爲例,假如目前某學院開設Java架構、大數據、人工智能等課程,通常的寫法:
public interface ICourse {
/** 錄製視頻 */
public void record();
}
public class JavaCourse implements ICourse {
@Override
public void record() {
System.out.println("錄製Java視頻");
}
}
public class PythonCourse implements ICourse {
@Override
public void record() {
System.out.println("錄製Python視頻");
}
}
public class Test {
public static void main(String[] args) {
ICourse javaCourse = new JavaCourse();
javaCourse.record();
ICourse pythonCourse = new PythonCourse();
pythonCourse.record();
}
}
Test類需要依賴JavaCourse和PythonCourse,如果還有別的課程,那麼Test還將繼續依賴,違背了迪米特原則。
現在用簡單工廠模式進行優化,首先創建一個CourseFactory工廠類。
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 class SimpleFactoryTest {
public static void main(String[] args) {
CourseFactory courseFactory = new CourseFactory();
courseFactory.create("java");
courseFactory.create("python");
}
}
客戶端調用時變簡單了,但是工廠類的設計不符合開閉原則,因爲如果要增加前端課程,那麼工廠的create()方法就要修改代碼,繼續優化。
public class CourseFactory {
public ICourse create(String className) {
if(!(null == className || "".equals(className))) {
try {
return (ICourse) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
public class SimpleFactoryTest {
public static void main(String[] args) {
CourseFactory courseFactory = new CourseFactory();
ICourse course = courseFactory.create("cn.itdjn.ch2.ch2_2.ch2_2_2.JavaCourse");
course.record();
}
}
現在不需要修改CourseFactory中的代碼,但方法參數是字符串,可控性有待提升,而且還需要強制類型轉換,繼續優化。
public class CourseFactory {
public ICourse create(Class<? extends ICourse> clazz) {
try {
if(null != clazz) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class SimpleFactoryTest {
public static void main(String[] args) {
CourseFactory courseFactory = new CourseFactory();
ICourse course = courseFactory.create(JavaCourse.class);
course.record();
}
}
工廠方法模式
定義一個創建對象的接口,讓實現這個接口的類決定實例化哪個類,工廠方法模型讓類的實例化推遲到子類中進行。簡單工廠模式中隨着產品鏈的豐富,可能會出現每個課程創建邏輯上的區別,這個時候單一的簡單工廠模式就會變得不易於維護。我們需要根據單一職責原則將職能區分,專人幹專事。此時Java課程由Java工廠創建,Python課程由Python工廠創建。
public interface ICourseFactory {
ICourse create();
}
public class JavaCourseFactory implements ICourseFactory {
@Override
public ICourse create() {
return new JavaCourse();
}
}
public class PythonCourseFactory implements ICourseFactory {
@Override
public ICourse create() {
return new PythonCourse();
}
}
public class Test {
public static void main(String[] args) {
ICourseFactory factory = new PythonCourseFactory();
ICourse course = factory.create();
course.record();
factory = new JavaCourseFactory();
course = factory.create();
course.record();
}
}
抽象工廠模式
先解釋什麼是產品族,上圖第一行假設都是美的公司生產的家用電器,那麼正方形表示美的冰箱,圓形表示美的空調,菱形表示美的熱水器。那麼第二行可能就是格力冰箱、格力空調、格力熱水器,依次類推。
在解釋什麼產品等級結構,上圖第一列都是冰箱,第二列都是空調,第三列都是熱水器,但是每行屬於不同的廠家製造的。那廠家不同,工廠也就不一樣,所以延伸出下面的圖形。
由上圖可以看出,第一行的美的產品是由美的工廠生產的,第二行的格力產品是由格力工廠生產的,這兩個工廠是不同的具體工廠。
以上面的電器爲例,假如現在要製造冰箱、空調、洗衣機,那麼這三個產品是三個不同的產品等級結構。
冰箱、空調、洗衣機接口如下:
/**
* 空調
*/
public interface IAirConditioner {
void refrigeration(); // 製冷
}
/**
* 冰箱
*/
public interface IRefrigerator {
void coldStorage(); // 冷藏
}
/**
* 洗衣機
*/
public interface IWashingMachine {
void washClothes(); // 洗衣服
}
抽象工廠類:
/**
* 抽象工廠
*/
public interface HomeApplianceFactory {
IRefrigerator createRefrigerator();
IAirConditioner createAirConditioner();
IWashingMachine createWashingMachine();
}
具體不同廠家的冰箱、空調、洗衣機:
/**
* 美的空調
*/
public class BeautifulAirConditioner implements IAirConditioner {
@Override
public void refrigeration() {
System.out.println("美的空調可以製冷");
}
}
/**
* 美的冰箱
*/
public class BeautifulRefrigerator implements IRefrigerator {
@Override
public void coldStorage() {
System.out.println("美的冰箱冷藏食物");
}
}
/**
* 美的洗衣機
*/
public class BeautifulWashingMachine implements IWashingMachine {
@Override
public void washClothes() {
System.out.println("美的洗衣機可以洗衣服");
}
}
/**
* 格力空調
*/
public class GreeAirConditioner implements IAirConditioner {
@Override
public void refrigeration() {
System.out.println("格力空調可以製冷");
}
}
/**
* 格力冰箱
*/
public class GreeRefrigerator implements IRefrigerator {
@Override
public void coldStorage() {
System.out.println("格力冰箱冷藏食物");
}
}
/**
* 格力洗衣機
*/
public class GreeWashingMachine implements IWashingMachine {
@Override
public void washClothes() {
System.out.println("格力洗衣機可以洗衣服");
}
}
具體不同廠家的工廠類:
/**
* 美的工廠
*/
public class BeautifulFactory implements HomeApplianceFactory {
@Override
public IRefrigerator createRefrigerator() {
return new BeautifulRefrigerator();
}
@Override
public IAirConditioner createAirConditioner() {
return new BeautifulAirConditioner();
}
@Override
public IWashingMachine createWashingMachine() {
return new BeautifulWashingMachine();
}
}
/**
* 格力工廠
*/
public class GreeFactory implements HomeApplianceFactory{
@Override
public IRefrigerator createRefrigerator() {
return new GreeRefrigerator();
}
@Override
public IAirConditioner createAirConditioner() {
return new GreeAirConditioner();
}
@Override
public IWashingMachine createWashingMachine() {
return new GreeWashingMachine();
}
}
測試類:
public class Test {
public static void main(String[] args) {
HomeApplianceFactory factory = new BeautifulFactory();
factory.createAirConditioner().refrigeration();
factory.createRefrigerator().coldStorage();
factory.createWashingMachine().washClothes();
factory = new GreeFactory();
factory.createAirConditioner().refrigeration();
factory.createRefrigerator().coldStorage();
factory.createWashingMachine().washClothes();
}
}
上述完整的描述了兩個產品族,但是如果繼續擴展產品等級,比如增加電風扇,那代碼從抽象工廠到具體工廠都要調整,這違背了開閉原則,因此抽象工廠也是有缺點的,但是是可接受的,只要不是過於頻繁的升級。