上一篇文章詳細學習了單例模式的多種寫法,今天來學習一下以下三種模式:簡單工廠、工廠方法、抽象工廠模式,其實簡單工廠模式不屬於 GOF 23 種設計模式,不過它實現簡單,在有些場景下它也比較適用,因此就首先來看一下它。java
一般咱們使用 new 關鍵字就能夠建立對象,爲何還要使用工廠模式呢?咱們如下面這個例子來看一下。git
若是有一個手機店,出售 IPhone、Samsung、Huawei 品牌的手機。github
public class Phone {
public void pay() {}
public void box() {}
}
class IPhone extends Phone {
}
class Samsung extends Phone {
}
class Huawei extends Phone {
}
複製代碼
顧客在購買手機的代碼能夠這樣寫:設計模式
public class PhoneStore {
public Phone buyPhone(String type) {
Phone phone = null;
if ("Iphone".equals(type)) {
phone = new IPhone();
} else if ("Samsung".equals(type)) {
phone = new Samsung();
} else if ("Huawei ".equals(type)) {
phone = new Huawei();
}
phone.pay();
phone.box();
return phone;
}
}
複製代碼
若是店鋪想要增長競爭力,又添加了幾種手機品牌,就須要去修改 buyPhone 方法,在其中繼續添加 if-else 語句。框架
也就是說,若是代碼有變化或擴展,就必須從新修改該方法,這就違反了對擴展開放、對修改關閉的原則。並且這樣修改對於系統來講,將難以維護和更新。dom
其實,咱們能夠將建立對象的代碼移到另外一個對象,封裝成一個工廠類,在添加或改變手機的品牌時,只須要修改該工廠類便可:ide
public class SimplePhoneFactory {
public static Phone createPhone(String type) {
Phone phone = null;
if ("Iphone".equals(type)) {
phone = new IPhone();
} else if ("Samsung".equals(type)) {
phone = new Samsung();
} else if ("Huawei ".equals(type)) {
phone = new Huawei();
}
return phone;
}
}
複製代碼
而 PhoneStore 的代碼就能夠修改成:學習
public class PhoneStore {
public Phone buyPhone(String type) {
Phone phone = SimplePhoneFactory.createPhone(type);
phone.pay();
phone.box();
return phone;
}
}
複製代碼
上述模式就是簡單工廠模式,也能夠利用靜態方法來定義工廠,這稱爲靜態工廠。ui
咱們來看一下它的 UML 圖:spa
下面來總結一下,簡單工廠模式有哪些優缺點。
優勢:
但它也有一些缺點:
Calendar#createCalendar
該方法部分源碼以下:
private static Calendar createCalendar(TimeZone zone, Locale aLocale) ··· if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
複製代碼
這裏會根據傳入的語言和國家來決定生成什麼 Calendar。
若是手機店規模比較大,但願開設 IPhone、Samsung、Huawei 專賣分店(假設分店本身製做手機),這應該如何擴展呢?
因爲購買手機的過程相似,都須要付款、打包;而分店則須要生產其相應品牌的手機。咱們能夠將 PhoneStore 修改成抽象類 ,將 SimplePhoneFactory 的 createPhone 方法改成抽象方法,放置到 AbstractPhoneStore 中。
public abstract class PhoneStore {
public Phone buyPhone() {
Phone phone = createPhone();
phone.pay();
phone.box();
return phone;
}
protected abstract Phone createPhone();
}
複製代碼
IPhone、Samsung、Huawei 三種產品分別以下:
public class Phone {
public void pay() { }
public void box() { }
}
class IPhone extends Phone {
}
class Samsung extends Phone {
}
class Huawei extends Phone {
}
複製代碼
三種產品對應的 IPhone、Samsung、Huawei 三家分店,它們的具體實現以下:
public class IPhoneStore extends PhoneStore {
@Override
protected Phone createPhone() {
return new IPhone();
}
}
public class SamsungStore extends PhoneStore {
@Override
protected Phone createPhone() {
return new Samsung();
}
}
public class HuaweiStore extends PhoneStore {
@Override
protected Phone createPhone() {
return new Huawei();
}
}
複製代碼
若是咱們要 IPhone 手機,代碼能夠以下:
public class Client {
public static void main(String[] args) {
PhoneStore phoneStore = new IPhoneStore();
Phone phone = phoneStore.buyPhone();
// phone 爲 IPhone
}
}
複製代碼
上述這種模式就是工廠方法模式,它會定義一個建立對象的接口,但讓實現這個接口的類來決定實例化哪一個類。例如這裏建立了一個 PhoneStore 抽象類,但實際上由 IPhoneStore 來決定實例化哪一個 Phone 的實現類。
咱們能夠看到工廠方法模式包括了四個角色:
它的 UML 圖以下:
下面總結一下工廠方法模式的優勢:
缺點:
簡單工廠模式與工廠方法模式之間的區別以下:
Java 集合的 iterator 方法就是一個工廠方法。部分集合的 UML 圖以下:
抽象工廠
該實例中抽象工廠就是 Iterable 接口:
public interface Iterable<T> {
Iterator<T> iterator();
}
複製代碼
具體工廠
具體工廠在 Java 集合中很是多,這裏舉兩個例子,例如在 ArrayList 中的實現:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable public Iterator<E> iterator() {
return new Itr();
}
}
複製代碼
在 HashMap 中的實現中,entrySet 方法返回一個 EntrySet 對象:
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
}
複製代碼
抽象產品
抽象產品就是 Iterator 接口
public interface Iterator<E> {
boolean hasNext();
E next();
}
複製代碼
具體產品
這裏的具體產品,以上面說的 ArrayList 中的 Itr 和 EntrySet 中的 EntryIterator 爲例。
Itr 對象以下:
private class Itr implements Iterator<E> {
public boolean hasNext() { ··· }
public E next() { ··· }
}
複製代碼
EntryIterator 對象以下:
abstract class HashIterator {
public final boolean hasNext() { ··· }
final Node<K,V> nextNode() { ··· }
}
final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
複製代碼
若是想要改變商業模式,三家專賣店內不只能夠賣 IPhone、Sansung、Huawei 三種品牌的手機,也能夠賣相應品牌的電腦(假設分店本身生產手機、電腦),這應該如何設計呢?(這個例子不太符合實際狀況,不過能說明抽象工廠模式的含義,湊合看吧)
店鋪能夠以下設計:
public abstract class PhoneStore {
public Phone buyPhone() {
Phone phone = createPhone();
phone.pay();
phone.box();
return phone;
}
public Computer buyComputer() {
Computer computer = createComputer();
computer.pay();
computer.pack();
return computer;
}
protected abstract Phone createPhone();
protected abstract Computer createComputer();
}
複製代碼
三種品牌的手機類以下:
public class Phone {
public void pay() { }
public void box() { }
}
class IPhone extends Phone {
}
class SamsungPhone extends Phone {
}
class HuaweiPhone extends Phone {
}
複製代碼
三種品牌的電腦類以下:
public class Computer {
public void pay() { }
public void pack() { }
}
class MacComputer extends Computer {
}
class SamsungComputer extends Computer {
}
class HuaweiComputer extends Computer {
}
複製代碼
對於三家相應品牌的專賣店,它們的具體實現以下:
public class IPhoneStore extends PhoneStore {
@Override
protected Phone createPhone() {
return new IPhone();
}
@Override
protected Computer createComputer() {
return new MacComputer();
}
}
public class SamsungStore extends PhoneStore {
@Override
protected Phone createPhone() {
return new SamsungPhone();
}
@Override
protected Computer createComputer() {
return new SamsungComputer();
}
}
public class HuaweiStore extends PhoneStore {
@Override
protected Phone createPhone() {
return new HuaweiPhone();
}
@Override
protected Computer createComputer() {
return new HuaweiComputer();
}
}
複製代碼
若是咱們要在 IPhone 專賣店購買手機和電腦,代碼能夠以下:
public class Test {
public static void main(String[] args) {
PhoneStore phoneStore = new IPhoneStore();
Phone phone = phoneStore.buyPhone();
Computer computer = phoneStore.buyComputer();
// phone 爲 IPhone
// computer 爲 Mac
}
}
複製代碼
上述的模式就是抽象工工廠模式,它提供了一個接口,用於建立一個產品的家族,而不須要指定具體類。每一個具體工廠會建立某個產品家族。
在上述例子,IPhoneStore、SamsungStore、HuaweiStore 就是一個個具體的工廠,它們能夠生產對應品牌的手機和電腦。其中 IPhoneStore 這個工廠就是建立 IPhone、MacComputer 這個產品家族。
它的 UML 圖以下:
下面總結一下抽象工廠模式的優缺點。
優勢:
缺點:
抽象工廠模式與工廠方法模式很相似,它們之間的區別以下:
它們之間也有一些關聯,就是抽象工廠的方法以工廠方法的方法來實現。在抽象工廠的接口中,每一個方法都負責建立一個具體產品,而具體工廠來提供具體的實現。
例如,PhoneStore 中的 createPhone、createComputer 方法由子類實現,這兩個方法單獨來看都是在建立一個對象,其實也就是一個工廠方法。
JDBC 中的 Connection 就是一個抽象工廠模式,在不一樣的鏈接池中有不一樣的實現,例如 druid 和 dbcp:
因爲本人對於 druid 和 dbcp 的實現也不太熟悉,這裏就很少解釋了,有興趣的小夥伴能夠本身研究一下。