工廠模式
簡介
工廠顧名思義就是生産産品,根據産品是具體産品還是具體工廠可分為簡單工廠模式和工廠方法模式,根據工廠的抽象程度可分為工廠方法模式和抽象工廠模式。該模式用于封裝和管理對象的建立,是一種建立型模式。本文從一個具體的例子逐漸深入分析,來體會三種工廠模式的應用場景和利弊。下面對這三種模式進行分析
1、簡單工廠模式
該模式對對象建立管理方式最為簡單,該模式通過向工廠傳遞類型來指定要建立的對象,其UML類圖如下:
下面我們使用生産手機來介紹該模式:
Phone類:手機标準規範類(AbstractProduct)
//标準接口
public interface Phone {
void make();
}
MiPhone類:制造小米手機(Product1)
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
System.out.println("生産小米手機");
}
}
ApplePhone類:制造蘋果手機(Product2)
public class ApplePhone implements Phone {
public ApplePhone() {
this.make();
}
@Override
public void make() {
System.out.println("生産蘋果手機");
}
}
PhoneFactory類:手機代工廠(Factory)
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("MiPhone")){
return new MiPhone();
}
else if(phoneType.equalsIgnoreCase("ApplePhone")) {
return new ApplePhone();
}
return null;
}
}
用戶端測試類:
public class Test {
public static void main(String[] arg) {
PhoneFactory factory = new PhoneFactory();
MiPhone miPhone = factory.makePhone("MiPhone");
ApplePhone applePhone = (ApplePhone)factory.makePhone("ApplePhone");
}
}
2、工廠方法模式(Factory Method)
和簡單工廠模式中工廠負責生産所有産品相比,工廠方法模式将生成具體産品的任務分發給具體的産品工廠,其UML類圖如下:
也就是定義一個抽象工廠,裡邊定義了産品的生産接口,但不負責具體的産品生産,将生産任務交給不同的派生類工廠。這樣不用通過指定類型來建立對象了。
接下來繼續使用生産手機的例子來介紹該模式。
其中和産品相關的Phone類、MiPhone類和ApplePhone類的定義不變。
AbstractFactory類:生産不同産品的工廠抽象類
public interface AbstractFactory {
Phone makePhone();
}
XiaoMiFactory類:生産小米手機的工廠(ConcreteFactory1)
public class XiaoMiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new MiPhone();
}
}
AppleFactory類:生産蘋果手機的工廠(ConcreteFactory2)
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
}
用戶端測試:
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
appleFactory.makePhone(); // make iphone!
}
}
3. 抽象工廠模式(Abstract Factory)
上面兩種模式不管工廠怎麼拆分抽象,都隻是針對單一産品Phone(AbstractProduct)來進行的,如果表示一個産品系列該怎麼處理呢?看下面的需求
【需求】用抽象工廠模式設計農場類。
分析:農場中除了可以養動物,還可以培養植物,如養馬、養牛、種菜、種水果等,是以本執行個體用抽象工廠模式來實作。
本例用抽象工廠模式來設計兩個農場,一個是韶關農場用于養牛和種菜,一個是上饒農場用于養馬和種水果,可以在以上兩個農場中定義一個生成動物的方法 newAnimal() 和一個培養植物的方法 newPlant()。
對馬類、牛類、蔬菜類和水果類等具體産品類,由于要顯示它們的圖像(點此下載下傳圖檔),是以它們的構造函數中用到了 JPanel、JLabel 和 ImageIcon 等元件,并定義一個 show() 方法來顯示它們。
用戶端程式通過對象生成器類 ReadXML 讀取 XML 配置檔案中的資料來決定養什麼動物和培養什麼植物(點此下載下傳 XML 檔案)。其結構圖如圖 3 所示。
圖3 農場類的結構圖
程式代碼如下:
//抽象工廠:農場類
interface Farm {
public Animal newAnimal();
public Plant newPlant();
}
//具體工廠:韶關農場類
class SGfarm implements Farm {
public Animal newAnimal() {
System.out.println("新牛出生!");
return new Cattle();
}
public Plant newPlant() {
System.out.println("蔬菜長成!");
return new Vegetables();
}
}
//具體工廠:上饒農場類
class SRfarm implements Farm {
public Animal newAnimal() {
System.out.println("新馬出生!");
return new Horse();
}
public Plant newPlant() {
System.out.println("水果長成!");
return new Fruitage();
}
}
//抽象産品:動物類
interface Animal {
public void show();
}
//具體産品:馬類
class Horse implements Animal {
JScrollPane sp;
JFrame jf = new JFrame("抽象工廠模式測試");
public Horse() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("動物:馬"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使用者點選視窗關閉
}
public void show() {
jf.setVisible(true);
}
}
//具體産品:牛類
class Cattle implements Animal {
JScrollPane sp;
JFrame jf = new JFrame("抽象工廠模式測試");
public Cattle() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("動物:牛"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Cattle.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使用者點選視窗關閉
}
public void show() {
jf.setVisible(true);
}
}
//抽象産品:植物類
interface Plant {
public void show();
}
//具體産品:水果類
class Fruitage implements Plant {
JScrollPane sp;
JFrame jf = new JFrame("抽象工廠模式測試");
public Fruitage() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("植物:水果"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/P_Fruitage.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使用者點選視窗關閉
}
public void show() {
jf.setVisible(true);
}
}
//具體産品:蔬菜類
class Vegetables implements Plant {
JScrollPane sp;
JFrame jf = new JFrame("抽象工廠模式測試");
public Vegetables() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("植物:蔬菜"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/P_Vegetables.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使用者點選視窗關閉
}
public void show() {
jf.setVisible(true);
}
}
//解析XML檔案類
class ReadXML {
public static Object getObject() {
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src/AbstractFactory/config.xml"));
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = "AbstractFactory." + classNode.getNodeValue();
System.out.println("新類名:" + cName);
Class<?> c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
//用戶端測試類
public class FarmTest {
public static void main(String[] args) {
try {
Farm f;
Animal a;
Plant p;
f = (Farm) ReadXML.getObject();
a = f.newAnimal();
p = f.newPlant();
a.show();
p.show();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
程式運作結果如圖 4 所示:
圖4 農場養殖的運作結果
模式的應用場景
抽象工廠模式通常适用于以下場景:
- 當需要建立的對象是一系列互相關聯或互相依賴的産品族時,如電器工廠中的電視機、洗衣機、空調等。
- 系統中有多個産品族,但每次隻使用其中的某一族産品。如有人隻喜歡穿某一個品牌的衣服和鞋。
- 系統中提供了産品的類庫,且所有産品的接口相同,用戶端不依賴産品執行個體的建立細節和内部結構。
總結:
上面介紹的三種工廠模式有各自的應用場景,實際應用時能解決問題滿足需求即可,可靈活變通,無所謂進階與低級。
此外無論哪種模式,由于可能封裝了大量對象和工廠建立,新加産品需要修改已定義好的工廠相關的類,是以對于産品和工廠的擴充不太友好,利弊需要權衡一下。