Java超詳細(xì)講解設(shè)計模式之一的工廠模式
工廠模式
在Java應(yīng)用程序中對象無處不在,這些對象都需要進(jìn)行創(chuàng)建,如果創(chuàng)建的時候直接new對象,那么如果我們要更換對象,所有new對象的地方都需要進(jìn)行更改。違背了軟件設(shè)計原則中的開閉原則。如果我們使用工廠生產(chǎn)對象,只需要在工廠中關(guān)注對象的改變即可,達(dá)到了與對象解耦的目的,工廠模式最大的特點(diǎn)就是解耦合
補(bǔ)充:
開閉原則: 對擴(kuò)展開放,對修改關(guān)閉。在程序需要進(jìn)行擴(kuò)展的時候,不能去修改原有的代碼,實現(xiàn)一個熱插拔的效果。是為了使程序的擴(kuò)展性好,易于維護(hù)和升級。
1.簡單工廠
1.1結(jié)構(gòu)
- 抽象產(chǎn)品: 定義了產(chǎn)品的規(guī)范,描述了產(chǎn)品的主要特性和功能。
- 具體產(chǎn)品: 實現(xiàn)或者繼承抽象產(chǎn)品的子類
- 具體工廠: 提供了創(chuàng)建產(chǎn)品的方法,調(diào)用者通過該方法來創(chuàng)建產(chǎn)品。
1.2實現(xiàn)
以點(diǎn)咖啡為例:
咖啡抽象類
public abstract class Coffee {
/**
* 獲取咖啡類型
* @return
*/
public abstract String getName();
/**
*加糖
*/
public void addSugar(){
System.out.println("加糖");
}
/**
*加奶
*/
public void addMilk(){
System.out.println("加奶");
}
}
美式咖啡類
public class AmericanCoffee extends Coffee{
@Override
public String getName(){
return "美式咖啡";
}
}
拿鐵咖啡類
public class LatteCoffee extends Coffee {
@Override
public String getName(){
return "拿鐵咖啡";
}
}
咖啡工廠類
public class CoffeeFactory {
public Coffee createCoffee(String type){
Coffee coffee = null;
if("american".equals(type)){
coffee = new AmericanCoffee();
}else if("latten".equals(type)){
coffee = new LatteCoffee();
}else{
throw new RuntimeException("沒有此類型的咖啡");
}
return coffee;
}
}
咖啡店類
public class CoffeeStore {
public Coffee orderCoffee(String type){
CoffeeFactory factory = new CoffeeFactory();
//調(diào)用生產(chǎn)咖啡的方法
Coffee coffee = factory.createCoffee(type);
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
測試類
public class Test {
public static void main(String[] args) {
CoffeeStore coffeeStore = new CoffeeStore();
Coffee coffee = coffeeStore.orderCoffee("latten");
System.out.println(coffee.getName());
}
}
類圖

咖啡工廠負(fù)責(zé)生產(chǎn)咖啡(具體工廠),咖啡店通過咖啡工廠選取咖啡
其實簡單工廠是大家在實際寫代碼的時候經(jīng)常用到的,雖然簡單工廠實現(xiàn)了咖啡店與咖啡的耦合,但是可以明顯看到咖啡與咖啡工廠又耦合起來了,后期如果增加咖啡的新品種,我們需要修改咖啡工廠的代碼,這又違背了“開閉原則”。
注意:
簡單工廠和不使用工廠是有很大區(qū)別的,如果咖啡店有多個,不使用工廠如果遇到新增咖啡需要修改所有咖啡店,但是使用工廠只需要修改咖啡工廠,類似于將所有咖啡店抽取出一個抽象的咖啡店。
1.3優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
封裝了創(chuàng)建對象的過程,可以通過參數(shù)直接獲取對象,把對象的創(chuàng)建和業(yè)務(wù)邏輯層分開,這樣可以避免之后修改客戶代碼,如果需要實現(xiàn)新產(chǎn)品直接修改工廠類,更容易擴(kuò)展。
缺點(diǎn):
增加新產(chǎn)品時還需要修改工廠類的代碼,違背了“開閉原則”。
1.4擴(kuò)展
靜態(tài)工廠,將工廠類中創(chuàng)建對象的功能定義為靜態(tài)的,這樣不需要再創(chuàng)建工廠類,直接通過類名調(diào)用靜態(tài)方法,類似于工具類
public class CoffeeFactory {
//靜態(tài)方法
public static Coffee createCoffee(String type){
Coffee coffee = null;
if("american".equals(type)){
coffee = new AmericanCoffee();
}else if("latten".equals(type)){
coffee = new LatteCoffee();
}else{
throw new RuntimeException("沒有此類型的咖啡");
}
return coffee;
}
}
2.工廠方法
對工廠進(jìn)行抽象,每一種產(chǎn)品對應(yīng)一個具體工廠,新增產(chǎn)品只需要再新增對應(yīng)的具體工廠,符合”開閉原則“
2.1結(jié)構(gòu)
- 抽象工廠: 提供創(chuàng)建產(chǎn)品的接口,調(diào)用者通過它訪問具體工廠的工廠方法來創(chuàng)建產(chǎn)品
- 具體工廠: 主要是實現(xiàn)抽象工廠中的抽象方法,完成具體產(chǎn)品的創(chuàng)建
- 抽象產(chǎn)品: 定義了產(chǎn)品的規(guī)范,描述了產(chǎn)品的主要特性和功能
- 具體產(chǎn)品: 實現(xiàn)抽象產(chǎn)品所定義的接口,由具體工廠來創(chuàng)建,與具體工廠之間一一對應(yīng)
2.2實現(xiàn)
抽象咖啡類和具體咖啡類不變
咖啡工廠(抽象工廠)
public interface CoffeeFactory {
/**
* 創(chuàng)建咖啡
* @return
*/
Coffee createCoffee();
}
美式咖啡工廠(具體工廠)
public class AmericanCoffeeFactory implements CoffeeFactory{
//美式咖啡工廠對象,專門生產(chǎn)美式咖啡
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
拿鐵咖啡工廠(具體工廠)
public class LatteCoffeeFactory implements CoffeeFactory{
//拿鐵咖啡工廠對象,專門生產(chǎn)拿鐵咖啡
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
}
咖啡店
public class CoffeeStore {
private CoffeeFactory factory;
public void setFactory(CoffeeFactory factory) {
this.factory = factory;
}
/**
* 點(diǎn)咖啡
*/
public Coffee orderCoffee() {
Coffee coffee = factory.createCoffee();
coffee.addSugar();
coffee.addMilk();
return coffee;
}
}
測試類
public class Test {
public static void main(String[] args) {
//創(chuàng)建咖啡店對象
CoffeeStore coffeeStore = new CoffeeStore();
//創(chuàng)建工廠對象
CoffeeFactory factory = new AmericanCoffeeFactory();
coffeeStore.setFactory(factory);
//點(diǎn)咖啡
Coffee coffee = coffeeStore.orderCoffee();
System.out.println(coffee.getName());
}
}
類圖

我們只需要知道所點(diǎn)咖啡具體對應(yīng)的工廠對象,通過咖啡店調(diào)用對應(yīng)的工廠,由工廠創(chuàng)建咖啡對象實現(xiàn)點(diǎn)咖啡的過程
2.3優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 用戶只需要知道具體工廠就可以獲得所需產(chǎn)品,無需知道產(chǎn)品的具體創(chuàng)建過程
- 在系統(tǒng)新增產(chǎn)品時只需要添加具體產(chǎn)品類和對應(yīng)的具體工廠類,無需對原工廠進(jìn)行修改符合“開閉原則”
缺點(diǎn):
每增加一個產(chǎn)品就要增加一個對應(yīng)的具體工廠類,增加的系統(tǒng)的復(fù)雜性。如果具體產(chǎn)品種類過多,那么大量的工廠類不僅難以管理,而且也會造成程序中創(chuàng)建的對象過多,嚴(yán)重影響內(nèi)存性能
3.抽象工廠
3.1結(jié)構(gòu)
- 抽象工廠: 提供創(chuàng)建產(chǎn)品的接口,包含多個創(chuàng)建產(chǎn)品的方法,可以創(chuàng)建多個不同等級的產(chǎn)品
- 具體工廠: 主要是實現(xiàn)抽象工廠中的多個抽象方法,完成具體產(chǎn)品的創(chuàng)建
- 抽象產(chǎn)品: 定義了產(chǎn)品的規(guī)范,描述了產(chǎn)品的主要特性和功能,抽象工廠模式有多個抽象產(chǎn)品
- 具體產(chǎn)品: 實現(xiàn)抽象產(chǎn)品所定義的接口,由具體工廠來創(chuàng)建,與具體工廠是多對一關(guān)系
3.2實現(xiàn)
咖啡抽象類
public abstract class Coffee {
/**
* 獲取咖啡類型
* @return
*/
public abstract String getName();
/**
*加糖
*/
public void addSugar(){
System.out.println("加糖");
}
/**
*加奶
*/
public void addMilk(){
System.out.println("加奶");
}
}
美式咖啡類
public class AmericanCoffee extends Coffee{
@Override
public String getName(){
return "美式咖啡";
}
}
拿鐵咖啡類
public class LatteCoffee extends Coffee {
@Override
public String getName(){
return "拿鐵咖啡";
}
}
甜品抽象類
public abstract class Dessert {
//甜品抽象類
public abstract void show();
}
抹茶慕斯類
public class MatchaMousse extends Dessert{
//抹茶慕斯類
@Override
public void show() {
System.out.println("抹茶慕斯");
}
}
提拉米蘇類
public class Tiramisu extends Dessert{
//提拉米蘇類
@Override
public void show() {
System.out.println("提拉米蘇");
}
}
甜品工廠
public interface DessertFactory {
/**
* 生產(chǎn)咖啡
* @return
*/
Coffee createCoffee();
/**
* 生產(chǎn)甜品
* @return
*/
Dessert createDessert();
}
美式風(fēng)味甜品工廠類
public class AmericanDessertFactory implements DessertFactory{
/**
*美式風(fēng)味甜品工廠
* 可以生產(chǎn)美式咖啡和抹茶慕斯
*/
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
@Override
public Dessert createDessert() {
return new MatchaMousse();
}
}
意大利風(fēng)味甜品工廠類
public class ItalyDessertFactory implements DessertFactory {
/**
*意大利風(fēng)味甜品工廠
* 可以生產(chǎn)拿鐵咖啡和提拉米蘇
*/
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
@Override
public Dessert createDessert() {
return new Tiramisu();
}
}
測試類
public class Test {
public static void main(String[] args) {
//ItalyDessertFactory factory = new ItalyDessertFactory();
AmericanDessertFactory factory = new AmericanDessertFactory();
Coffee coffee = factory.createCoffee()
Dessert dessert = factory.createDessert();
System.out.println(coffee.getName());
dessert.show();
}
}
類圖

由類圖可見,抽象工廠不再是一個具體工廠對應(yīng)一個產(chǎn)品,而是一個具體工廠對應(yīng)一個產(chǎn)品族。如果需要增加一個產(chǎn)品族只需加對應(yīng)的工廠類,符合”開閉原則“
3.3優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
在工廠方法的基礎(chǔ)上減少了部分對象的創(chuàng)建,適合于每次只使用同一產(chǎn)品族的對象這類應(yīng)用場景
缺點(diǎn):
當(dāng)產(chǎn)品族中需要增加一個產(chǎn)品時,所有工廠都要修改
4.模式擴(kuò)展
配置文件+簡單工廠
通過工廠模式+配置文件的方式解除工廠對象和產(chǎn)品對象的耦合。在工廠類中加載配置文件的全類名,通過反射創(chuàng)建對象并存儲在容器中,如果需要直接從容器中獲?。⊿pring IOC原理)
4.1實現(xiàn)
1.定義配置文件
american = com.xue.config_factory.AmericanCoffee latten = com.xue.config_factory.LatteCoffee
2.改進(jìn)工廠類
public class CoffeeFactory {
/**
* 加載配置文件,獲取配置文件中配置的全類名,并創(chuàng)建該類的對象進(jìn)行存儲
*/
//1.定義容器對象存儲咖啡對象
private static HashMap<String, Coffee> map = new HashMap<>();
//2.加載配置文件
static {
//創(chuàng)建 Properties對象
Properties properties = new Properties();
//調(diào)用properties對象中的load方法進(jìn)行配置文件的加載
InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
properties.load(is);
//從properties中獲取全類名
Set<Object> keys = properties.keySet();
for (Object key : keys) {
String className = properties.getProperty((String) key);
//通過反射創(chuàng)建對象
Class class1 = Class.forName(className);
Coffee coffee = (Coffee) class1.newInstance();
//將名稱和對象存儲在容器中
map.put((String) key,coffee);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//根據(jù)名稱獲取對象
public static Coffee createCoffee(String name) {
return map.get(name);
}
}
靜態(tài)成員變量用來存儲創(chuàng)建的對象(鍵存儲的是名稱,值存儲的是對應(yīng)的對象),而讀取配置文件和創(chuàng)建對象寫在靜態(tài)代碼塊中只需要執(zhí)行一次
測試類
public class Test {
public static void main(String[] args) {
Coffee coffee = CoffeeFactory.createCoffee("american");
System.out.println(coffee.getName());
System.out.println("------------");
Coffee latten = CoffeeFactory.createCoffee("latten");
System.out.println(latten.getName());
}
}
成功?。?!

以上就是Java設(shè)計模式——工廠模式的介紹及四種實現(xiàn)方式 往期鏈接:單例模式
到此這篇關(guān)于Java超詳細(xì)講解設(shè)計模式之一的工廠模式的文章就介紹到這了,更多相關(guān)Java 工廠模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用poi-tl1.9.1生成Word文檔的技巧分享
本文將簡單介紹poi-tl的相關(guān)知識,通過一個實際的案例實踐,充分介紹如何利用poi-tl進(jìn)行目標(biāo)文檔的生成,同時分享幾個不同的office版本如何進(jìn)行圖表生成的解決方案,需要的朋友可以參考下2023-09-09
Spring如何基于Proxy及cglib實現(xiàn)動態(tài)代理
這篇文章主要介紹了Spring如何基于Proxy及cglib實現(xiàn)動態(tài)代理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
示例解析java重載Overloading與覆蓋Overriding
這篇文章主要介紹了java重載Overloading與覆蓋Overriding的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
基于Java語言MD5加密Base64轉(zhuǎn)換方法
這篇文章主要為大家詳細(xì)介紹了基于Java語言的MD5加密Base64轉(zhuǎn)換方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09

