“嗨,阿米戈!”

“嗨,比拉博!”

“我們今天的話題不僅僅是有趣——它將是徹頭徹尾的史詩。”

“今天我要告訴你什麼是設計模式。

“太棒了!我聽說過很多關於他們的事。我等不及了!”

“有經驗的程序員必須編寫很多類。但這項工作最困難的部分是決定創建哪些類以及如何在它們之間分配工作。”

“他們解決這些問題的次數越多,他們就越意識到有些解決方案是好的,而另一些則不好。”

“糟糕的解決方案通常會產生比解決的問題更多的問題。它們擴展性差,造成許多不必要的限制等。而好的解決方案恰恰相反。”

“你能打個比方嗎?”

“假設你正在蓋房子。你正在考慮它的材料。你決定你需要牆壁、地板和天花板。結果,你蓋了一個平屋頂的房子,沒有“地基。這樣的房子會開裂,屋頂會漏水。這是一個糟糕的解決方案。”

“相反,由地基、牆壁和人字形屋頂組成的房屋將是一個很好的解決方案。下大雪沒有問題,因為雪會從屋頂上滑落。移動的土壤也不用擔心,因為地基將確保穩定性。我們認為這樣的解決方案很好。”

“我明白了。謝謝。”

“好。那我繼續。”

“隨著時間的推移,好的解決方案被稱為設計模式,而壞的解決方案被稱為反模式。”

“設計模式就像一個問題的答案。如果你從未聽過這個問題,就很難理解。”

第一類模式是創建模式。此類模式描述了與創建對象相關的良好解決方案。”

“創建對像有什麼複雜的?”

“碰巧的是,這正是我們現在要探索的。”

單例模式。

設計模式:單例、工廠、工廠方法、抽象工廠 - 1

“經常在程序中,一些對像只能存在一個副本。例如,控制台、記錄器、垃圾收集器等。”

糟糕的解決方案:不創建任何對象——只創建一個方法都是靜態的類。”

好的解決方案: 創建單個對象並將其存儲在靜態變量中。防止創建該類的第二個對象。例如:”

例子
class Sun
{
 private static Sun instance;
 public static Sun getInstance()
 {
  if (instance == null)
  instance = new Sun();

  return instance;
 }

 private Sun()
 {
 }
}
怎麼稱呼
Sun sun = Sun.getInstance();

“這很簡單。”

“首先,我們將構造函數設為私有。現在它只能從我們的類內部調用。除了 Sun 類的方法之外,我們已經阻止在任何地方創建 Sun 對象。”

“其次,這個類的一個對像只能通過調用getInstance()方法來獲取,這不僅是唯一可以創建Sun對象的方法,而且還保證了只有一個這樣的對象。”

“我懂了。”

“當你想,«現在,我到底該怎麼做?»,一個模式說,«你可以試試這個——這是最好的解決方案之一。»”

“謝謝。現在事情開始明朗了。”

“您還可以 在此處閱讀有關此模式的信息。”

工廠模式。

設計模式:單例、工廠、工廠方法、抽象工廠 - 2

“這是程序員經常遇到的情況。你有一些基類和許多子類。例如,一個遊戲角色類 (GamePerson) 和繼承它的所有其他角色的類。”

“假設您有以下課程:”

例子
abstract class GamePerson
{
}

class Warrior extends GamePerson
{
}

class Mag extends GamePerson
{
}

class Troll extends GamePerson
{
}

class Elf extends GamePerson
{
}

“問題是我們如何靈活方便地管理這些類對象的創建。”

“如果這個問題對你來說似乎很牽強,想像一下,在遊戲中你需要創建幾十把劍和盾牌,數百個魔法咒語,以及成千上萬的怪物。你不能沒有一個方便的方法來創建對象。 “

工廠模式提供了一個很好的解決方案。”

“首先,我們需要創建一個枚舉,其值對應於各種類。”

“其次,創建一個特殊的Factory類,該類具有基於枚舉值創建對象的靜態方法。”

“例如:”

例子
public enum PersonType
{
 UNKNOWN,
 WARRIOR,
 MAG,
 TROLL,
 ELF,
}

public class PersonFactory
{
 public static GamePerson createPerson(PersonType personType)
 {
  switch(personType)
  {
   WARRIOR:
   return new Warrior();
   MAG:
   return new Mag();
   TROLL:
   return new Troll();
   ELF:
   return new Elf();
   default:
   throw new GameException();
  }
 }
}
怎麼稱呼
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

“也就是說,我們創建了一個專門的類來管理對象的創建?”

“是的。”

“那麼這有什麼好處呢?”

“首先,在這個類中,對象可以用必要的數據初始化。”

“其次,您可以根據需要在方法之間盡可能多地傳遞所需的枚舉值,以便最終創建所需的對象。”

“第三,枚舉字段的數量不必與類的數量相匹配。字符的類型可能很多,但類很少。”

“例如,Mag & Warrior可能使用一個類(人類),但具有不同的強度和魔法屬性(構造函數參數)。”

“這是它可能的樣子(為了清楚起見,我還添加了黑暗精靈):”

例子
public enum PersonType
{
 UNKNOWN,
 WARRIOR,
 MAG,
 TROLL,
 ELF,
 DARK_ELF
}

public class PersonFactory
{
 public static GamePerson createPerson(PersonType personType)
 {
  switch(personType)
  {
   WARRIOR:
   return new Human(10, 0); // Strength, magic
   MAG:
   return new Human(0, 10); // Strength, magic
   TROLL:
   OGR:
   return new Troll();
   ELF:
   return new Elf(true); // true – good, false – evil
   DARK_ELF:
   return new Elf(false); // true – good, false – evil
   default:
   throw new GameException();
  }
 }
}
怎麼稱呼
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

“在上面的例子中,我們只使用了三個類來創建六種不同類型的對象。這非常方便。而且,我們將所有這些邏輯都集中在一個類和一個方法中。”

“現在假設我們決定為 Ogre 創建一個單獨的類——我們只需在這裡更改幾行代碼,而不是應用程序的一半。”

“我同意。這是一個很好的解決方案。”

“這就是我所說的:設計模式是好的解決方案的集合。”

“我也希望我知道在哪裡使用它們……”

“是的。我同意,你不會馬上明白。不過,知道但不能做總比不知道但不能做要好。關於這種模式,這裡有另一個對你有用的鏈接:工廠模式

“哦謝謝。”

抽象工廠模式。”

“有時當你有很多對象時,為工廠創建工廠的想法就會自動出現。這樣的工廠稱為抽象工廠。”

“哪裡需要這個?!”

“假設你有幾組相同的對象。用一個例子更容易說明。”

“現在,假設你的遊戲中有三個種族:人類、精靈和惡魔。為了平衡起見,每個種族都有戰士、弓箭手和法師。玩家只能創建屬於他或她正在玩的種族的物品在遊戲中。這是它在代碼中的樣子:“

兵種宣言
class Warrior
{
}
class Archer
{
}
class Mag
{
}
人類
class HumanWarrior extends Warrior
{
}

class HumanArcher extends Archer
{
}

class HumanMag extends Mag
{
}
精靈
class ElfWarrior extends Warrior
{
}

class ElfArcher extends Archer
{
}

class ElfMag extends Mag
{
}
惡魔
class DaemonWarrior extends Warrior
{
}

class DaemonArcher extends Archer
{
}

class DaemonMag extends Mag
{
}

現在讓我們創建種族,或者我們也可以稱他們為軍隊。

軍隊
abstract class Army
{
 public Warrior createWarrior();
 public Archer createArcher();
 public Mag createMag();
}
人類軍隊
class HumanArmy extends Army
{
 public Warrior createWarrior()
 {
  return new HumanWarrior();
 }
 public Archer createArcher()
 {
  return new HumanArcher();
 }
 public Mag createMag()
 {
  return new HumanMag();
 }
}
精靈軍隊
class ElfArmy extends Army
{
 public Warrior createWarrior()
 {
  return new ElfWarrior();
 }
 public Archer createArcher()
 {
  return new ElfArcher();
 }
 public Mag createMag()
 {
  return new ElfMag();
 }
}
魔軍
class DaemonArmy extends Army
{
 public Warrior createWarrior()
 {
  return new DaemonWarrior();
 }
 public Archer createArcher()
 {
  return new DaemonArcher();
 }
 public Mag createMag()
 {
  return new DaemonMag();
 }
}

“但是你怎麼用這個?”

“您可以在程序中的任何地方使用 Army、Warrior、Archer 和 Mage 類,並創建必要的對象——只需傳遞所需 Army 子類的對象即可。”

“例如:”

例子
Army humans = new HumanArmy();
Army daemons = new DaemonArmy();

Army winner = FightSimulator.simulate(humans, daemons);

“在上面的例子中,我們有一個模擬不同種族(軍隊)之間的戰鬥的類。你只需要傳遞兩個 Army 對象。類本身使用它們來創建各種部隊並在它們之間進行虛擬戰鬥以識別勝利者”

“我明白了。謝謝。一個非常有趣的方法。”

“一個好的解決方案,不管你說什麼。”

“是的。”

“這是關於該主題的另一個很好的鏈接: 抽象工廠模式