“嗨,阿米戈!”
“嗨,比拉博!”
“我们今天的话题不仅仅是有趣——它将是彻头彻尾的史诗。”
“今天我要告诉你什么是设计模式。 ”
“太棒了!我听说过很多关于他们的事。我等不及了!”
“有经验的程序员必须编写很多类。但这项工作最困难的部分是决定创建哪些类以及如何在它们之间分配工作。”
“他们解决这些问题的次数越多,他们就越意识到有些解决方案是好的,而另一些则不好。”
“糟糕的解决方案通常会产生比解决的问题更多的问题。它们扩展性差,造成许多不必要的限制等。而好的解决方案恰恰相反。”
“你能打个比方吗?”
“假设你正在盖房子。你正在考虑它的材料。你决定你需要墙壁、地板和天花板。结果,你盖了一个平屋顶的房子,没有“地基。这样的房子会开裂,屋顶会漏水。这是一个糟糕的解决方案。”
“相反,由地基、墙壁和人字形屋顶组成的房屋将是一个很好的解决方案。下大雪没有问题,因为雪会从屋顶上滑落。移动的土壤也不用担心,因为地基将确保稳定性。我们认为这样的解决方案很好。”
“我明白了。谢谢。”
“好。那我继续。”
“随着时间的推移,好的解决方案被称为设计模式,而坏的解决方案被称为反模式。”
“设计模式就像一个问题的答案。如果你从未听过这个问题,就很难理解。”
“第一类模式是创建模式。此类模式描述了与创建对象相关的良好解决方案。”
“创建对象有什么复杂的?”
“碰巧的是,这正是我们现在要探索的。”
单例模式。
“经常在程序中,一些对象只能存在一个副本。例如,控制台、记录器、垃圾收集器等。”
“糟糕的解决方案:不创建任何对象——只创建一个方法都是静态的类。”
“好的解决方案: 创建单个对象并将其存储在静态变量中。防止创建该类的第二个对象。例如:”
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对象的方法,而且还保证了只有一个这样的对象。”
“我懂了。”
“当你想,«现在,我到底该怎么做?»,一个模式说,«你可以试试这个——这是最好的解决方案之一。»”
“谢谢。现在事情开始明朗了。”
“您也可以 在此处阅读有关此模式的信息。”
工厂模式。
“这是程序员经常遇到的情况。你有一些基类和许多子类。例如,一个游戏角色类 (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 对象。类本身使用它们来创建各种部队并在它们之间进行虚拟战斗以识别胜利者”
“我明白了。谢谢。一个非常有趣的方法。”
“一个好的解决方案,不管你说什么。”
“是的。”
“这是关于该主题的另一个很好的链接: 抽象工厂模式”
GO TO FULL VERSION