游戏中所有的种族都有几乎同类的单位和建筑。 因此你可以在不同的种族上复用相同的 AI 结构, 同时还需要具备重写一些细节的能力。 通过这种方式, 你可以重写半兽人的 AI 使其更富攻击性, 也可以让人类侧重防守, 还可以禁止怪物建造建筑。 在游戏中新增种族需要创建新的 AI 子类, 还需要重写 AI 基类中所声明的默认方法。
// 具体类必须实现基类中的所有抽象操作,但是它们不能重写模板方法自身。 class OrcsAI extends GameAI is method buildStructures() is if (there are some resources) then // 建造农场,接着是谷仓,然后是要塞。
method buildUnits() is if (there are plenty of resources) then if (there are no scouts) // 建造苦工,将其加入侦查编组。 else // 建造兽族步兵,将其加入战士编组。
// ...
method sendScouts(position) is if (scouts.length > 0) then // 将侦查编组送到指定位置。
method sendWarriors(position) is if (warriors.length > 5) then // 将战斗编组送到指定位置。
// 子类可以重写部分默认的操作。 class MonstersAI extends GameAI is method collectResources() is // 怪物不会采集资源。
namespaceRefactoringGuru.DesignPatterns.TemplateMethod.Conceptual { // The Abstract Class defines a template method that contains a skeleton of // some algorithm, composed of calls to (usually) abstract primitive // operations. // // Concrete subclasses should implement these operations, but leave the // template method itself intact. abstractclassAbstractClass { // The template method defines the skeleton of an algorithm. publicvoidTemplateMethod() { this.BaseOperation1(); this.RequiredOperations1(); this.BaseOperation2(); this.Hook1(); this.RequiredOperation2(); this.BaseOperation3(); this.Hook2(); }
// These operations already have implementations. protectedvoidBaseOperation1() { Console.WriteLine("AbstractClass says: I am doing the bulk of the work"); }
protectedvoidBaseOperation2() { Console.WriteLine("AbstractClass says: But I let subclasses override some operations"); }
protectedvoidBaseOperation3() { Console.WriteLine("AbstractClass says: But I am doing the bulk of the work anyway"); } // These operations have to be implemented in subclasses. protectedabstractvoidRequiredOperations1();
protectedabstractvoidRequiredOperation2(); // These are "hooks." Subclasses may override them, but it's not // mandatory since the hooks already have default (but empty) // implementation. Hooks provide additional extension points in some // crucial places of the algorithm. protectedvirtualvoidHook1() { }
protectedvirtualvoidHook2() { } }
// Concrete classes have to implement all abstract operations of the base // class. They can also override some operations with a default // implementation. classConcreteClass1 : AbstractClass { protectedoverridevoidRequiredOperations1() { Console.WriteLine("ConcreteClass1 says: Implemented Operation1"); }
classClient { // The client code calls the template method to execute the algorithm. // Client code does not have to know the concrete class of an object it // works with, as long as it works with objects through the interface of // their base class. publicstaticvoidClientCode(AbstractClass abstractClass) { // ... abstractClass.TemplateMethod(); // ... } }
classProgram { staticvoidMain(string[] args) { Console.WriteLine("Same client code can work with different subclasses:");
Client.ClientCode(new ConcreteClass1());
Console.Write("\n"); Console.WriteLine("Same client code can work with different subclasses:"); Client.ClientCode(new ConcreteClass2()); } } }
Output.txt: 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass1 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass1 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass2 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass2 says: Overridden Hook1 ConcreteClass2 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
在 C++ 中使用模式
复杂度: ★☆☆
流行度: ★★☆
使用示例: 模版方法模式在 C++ 框架中很常见。 开发者通常使用它来向框架用户提供通过继承实现的、 对标准功能进行扩展的简单方式。
/** * The Abstract Class defines a template method that contains a skeleton of some * algorithm, composed of calls to (usually) abstract primitive operations. * * Concrete subclasses should implement these operations, but leave the template * method itself intact. */ classAbstractClass { /** * The template method defines the skeleton of an algorithm. */ public: voidTemplateMethod()const{ this->BaseOperation1(); this->RequiredOperations1(); this->BaseOperation2(); this->Hook1(); this->RequiredOperation2(); this->BaseOperation3(); this->Hook2(); } /** * These operations already have implementations. */ protected: voidBaseOperation1()const{ std::cout << "AbstractClass says: I am doing the bulk of the work\n"; } voidBaseOperation2()const{ std::cout << "AbstractClass says: But I let subclasses override some operations\n"; } voidBaseOperation3()const{ std::cout << "AbstractClass says: But I am doing the bulk of the work anyway\n"; } /** * These operations have to be implemented in subclasses. */ virtualvoidRequiredOperations1()const= 0; virtualvoidRequiredOperation2()const= 0; /** * These are "hooks." Subclasses may override them, but it's not mandatory * since the hooks already have default (but empty) implementation. Hooks * provide additional extension points in some crucial places of the * algorithm. */ virtualvoidHook1()const{} virtualvoidHook2()const{} }; /** * Concrete classes have to implement all abstract operations of the base class. * They can also override some operations with a default implementation. */ classConcreteClass1 : public AbstractClass { protected: voidRequiredOperations1()constoverride{ std::cout << "ConcreteClass1 says: Implemented Operation1\n"; } voidRequiredOperation2()constoverride{ std::cout << "ConcreteClass1 says: Implemented Operation2\n"; } }; /** * Usually, concrete classes override only a fraction of base class' operations. */ classConcreteClass2 : public AbstractClass { protected: voidRequiredOperations1()constoverride{ std::cout << "ConcreteClass2 says: Implemented Operation1\n"; } voidRequiredOperation2()constoverride{ std::cout << "ConcreteClass2 says: Implemented Operation2\n"; } voidHook1()constoverride{ std::cout << "ConcreteClass2 says: Overridden Hook1\n"; } }; /** * The client code calls the template method to execute the algorithm. Client * code does not have to know the concrete class of an object it works with, as * long as it works with objects through the interface of their base class. */ voidClientCode(AbstractClass *class_){ // ... class_->TemplateMethod(); // ... }
intmain(){ std::cout << "Same client code can work with different subclasses:\n"; ConcreteClass1 *concreteClass1 = new ConcreteClass1; ClientCode(concreteClass1); std::cout << "\n"; std::cout << "Same client code can work with different subclasses:\n"; ConcreteClass2 *concreteClass2 = new ConcreteClass2; ClientCode(concreteClass2); delete concreteClass1; delete concreteClass2; return0; }
Output.txt: 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass1 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass1 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass2 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass2 says: Overridden Hook1 ConcreteClass2 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
/** * Base class of social network. */ publicabstractclassNetwork { String userName; String password;
Network() {}
/** * Publish the data to whatever network. */ publicbooleanpost(String message) { // Authenticate before posting. Every network uses a different // authentication method. if (logIn(this.userName, this.password)) { // Send the post data. booleanresult= sendData(message.getBytes()); logOut(); return result; } returnfalse; }
/** * The Abstract Class defines a template method that contains a skeleton of some * algorithm, composed of calls to (usually) abstract primitive operations. * * Concrete subclasses should implement these operations, but leave the template * method itself intact. */ abstractclassAbstractClass { /** * The template method defines the skeleton of an algorithm. */ finalpublicfunctiontemplateMethod(): void { $this->baseOperation1(); $this->requiredOperations1(); $this->baseOperation2(); $this->hook1(); $this->requiredOperation2(); $this->baseOperation3(); $this->hook2(); }
/** * These operations already have implementations. */ protectedfunctionbaseOperation1(): void { echo"AbstractClass says: I am doing the bulk of the work\n"; }
protectedfunctionbaseOperation2(): void { echo"AbstractClass says: But I let subclasses override some operations\n"; }
protectedfunctionbaseOperation3(): void { echo"AbstractClass says: But I am doing the bulk of the work anyway\n"; }
/** * These operations have to be implemented in subclasses. */ abstractprotectedfunctionrequiredOperations1(): void;
/** * These are "hooks." Subclasses may override them, but it's not mandatory * since the hooks already have default (but empty) implementation. Hooks * provide additional extension points in some crucial places of the * algorithm. */ protectedfunctionhook1(): void{ }
protectedfunctionhook2(): void{ } }
/** * Concrete classes have to implement all abstract operations of the base class. * They can also override some operations with a default implementation. */ classConcreteClass1extendsAbstractClass { protectedfunctionrequiredOperations1(): void { echo"ConcreteClass1 says: Implemented Operation1\n"; }
/** * The client code calls the template method to execute the algorithm. Client * code does not have to know the concrete class of an object it works with, as * long as it works with objects through the interface of their base class. */ functionclientCode(AbstractClass $class) { // ... $class->templateMethod(); // ... }
echo"Same client code can work with different subclasses:\n"; clientCode(newConcreteClass1()); echo"\n";
echo"Same client code can work with different subclasses:\n"; clientCode(newConcreteClass2());
Output.txt: 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Same client code can work with different subclasses: AbstractClass says: I am doing bulk of the work ConcreteClass1 says: Implemented Operation1 AbstractClass says: But I let subclasses to override some operations ConcreteClass1 says: Implemented Operation2 AbstractClass says: But I am doing bulk of the work anyway
Same client code can work with different subclasses: AbstractClass says: I am doing bulk of the work ConcreteClass2 says: Implemented Operation1 AbstractClass says: But I let subclasses to override some operations ConcreteClass2 says: Overridden Hook1 ConcreteClass2 says: Implemented Operation2 AbstractClass says: But I am doing bulk of the work anyway
/** * The Abstract Class defines the template method and declares all its steps. */ abstractclassSocialNetwork { protected $username;
protected $password;
public function __construct(string $username, string $password) { $this->username = $username; $this->password = $password; }
/** * The actual template method calls abstract steps in a specific order. A * subclass may implement all of the steps, allowing this method to actually * post something to a social network. */ public function post(string $message): bool { // Authenticate before posting. Every network uses a different // authentication method. if ($this->logIn($this->username, $this->password)) { // Send the post data. All networks have different APIs. $result = $this->sendData($message); // ... $this->logOut();
return $result; }
returnfalse; }
/** * The steps are declared abstract to force the subclasses to implement them * all. */ abstractpublic function logIn(string $userName, string $password): bool;
abstractpublic function sendData(string $message): bool;
abstractpublic function logOut(): void; }
/** * This Concrete Class implements the Facebook API (all right, it pretends to). */ classFacebookextendsSocialNetwork { public function logIn(string $userName, string $password): bool { echo "\nChecking user's credentials...\n"; echo "Name: " . $this->username . "\n"; echo "Password: " . str_repeat("*", strlen($this->password)) . "\n";
simulateNetworkLatency();
echo "\n\nFacebook: '" . $this->username . "' has logged in successfully.\n";
returntrue; }
public function sendData(string $message): bool { echo "Facebook: '" . $this->username . "' has posted '" . $message . "'.\n";
returntrue; }
public function logOut(): void { echo "Facebook: '" . $this->username . "' has been logged out.\n"; } }
/** * This Concrete Class implements the Twitter API. */ classTwitterextendsSocialNetwork { public function logIn(string $userName, string $password): bool { echo "\nChecking user's credentials...\n"; echo "Name: " . $this->username . "\n"; echo "Password: " . str_repeat("*", strlen($this->password)) . "\n";
simulateNetworkLatency();
echo "\n\nTwitter: '" . $this->username . "' has logged in successfully.\n";
returntrue; }
public function sendData(string $message): bool { echo "Twitter: '" . $this->username . "' has posted '" . $message . "'.\n";
returntrue; }
public function logOut(): void { echo "Twitter: '" . $this->username . "' has been logged out.\n"; } }
/** * A little helper function that makes waiting times feel real. */ function simulateNetworkLatency() { $i = 0; while ($i < 5) { echo "."; sleep(1); $i++; } }
echo "\nChoose the social network to post the message:\n" . "1 - Facebook\n" . "2 - Twitter\n"; $choice = readline();
// Now, let's create a proper social network object and send the message. if ($choice == 1) { $network = newFacebook($username, $password); } elseif ($choice == 2) { $network = newTwitter($username, $password); } else { die("Sorry, I'm not sure what you mean by that.\n"); } $network->post($message);
classAbstractClass(ABC): """ The Abstract Class defines a template method that contains a skeleton of some algorithm, composed of calls to (usually) abstract primitive operations. Concrete subclasses should implement these operations, but leave the template method itself intact. """
deftemplate_method(self) -> None: """ The template method defines the skeleton of an algorithm. """
# These are "hooks." Subclasses may override them, but it's not mandatory # since the hooks already have default (but empty) implementation. Hooks # provide additional extension points in some crucial places of the # algorithm.
defhook1(self) -> None: pass
defhook2(self) -> None: pass
classConcreteClass1(AbstractClass): """ Concrete classes have to implement all abstract operations of the base class. They can also override some operations with a default implementation. """
defclient_code(abstract_class: AbstractClass) -> None: """ The client code calls the template method to execute the algorithm. Client code does not have to know the concrete class of an object it works with, as long as it works with objects through the interface of their base class. """
# ... abstract_class.template_method() # ...
if __name__ == "__main__": print("Same client code can work with different subclasses:") client_code(ConcreteClass1()) print("")
print("Same client code can work with different subclasses:") client_code(ConcreteClass2())
Output.txt: 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass1 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass1 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass2 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass2 says: Overridden Hook1 ConcreteClass2 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
# The Abstract Class defines a template method that contains a skeleton of some # algorithm, composed of calls to (usually) abstract primitive operations. # # Concrete subclasses should implement these operations, but leave the template # method itself intact. classAbstractClass # The template method defines the skeleton of an algorithm. deftemplate_method base_operation1 required_operations1 base_operation2 hook1 required_operations2 base_operation3 hook2 end
# These operations already have implementations.
defbase_operation1 puts 'AbstractClass says: I am doing the bulk of the work' end
defbase_operation2 puts 'AbstractClass says: But I let subclasses override some operations' end
defbase_operation3 puts 'AbstractClass says: But I am doing the bulk of the work anyway' end
# These operations have to be implemented in subclasses. defrequired_operations1 raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
# @abstract defrequired_operations2 raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
# These are "hooks." Subclasses may override them, but it's not mandatory # since the hooks already have default (but empty) implementation. Hooks # provide additional extension points in some crucial places of the algorithm.
defhook1; end
defhook2; end end
# Concrete classes have to implement all abstract operations of the base class. # They can also override some operations with a default implementation. classConcreteClass1 < AbstractClass defrequired_operations1 puts 'ConcreteClass1 says: Implemented Operation1' end
defrequired_operations2 puts 'ConcreteClass1 says: Implemented Operation2' end end
# Usually, concrete classes override only a fraction of base class' operations. classConcreteClass2 < AbstractClass defrequired_operations1 puts 'ConcreteClass2 says: Implemented Operation1' end
defrequired_operations2 puts 'ConcreteClass2 says: Implemented Operation2' end
defhook1 puts 'ConcreteClass2 says: Overridden Hook1' end end
# The client code calls the template method to execute the algorithm. Client # code does not have to know the concrete class of an object it works with, as # long as it works with objects through the interface of their base class. defclient_code(abstract_class) # ... abstract_class.template_method # ... end
puts 'Same client code can work with different subclasses:' client_code(ConcreteClass1.new) puts "\n"
puts 'Same client code can work with different subclasses:' client_code(ConcreteClass2.new)
output.txt: 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass1 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass1 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass2 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass2 says: Overridden Hook1 ConcreteClass2 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
在 Swift 中使用模式
复杂度: ★☆☆
流行度: ★★☆
使用示例: 模版方法模式在 Swift 框架中很常见。 开发者通常使用它来向框架用户提供通过继承实现的、 对标准功能进行扩展的简单方式。
/// The Abstract Protocol and its extension defines a template method that /// contains a skeleton of some algorithm, composed of calls to (usually) /// abstract primitive operations. /// /// Concrete subclasses should implement these operations, but leave the /// template method itself intact. protocolAbstractProtocol {
/// The template method defines the skeleton of an algorithm. functemplateMethod()
/// These operations already have implementations. funcbaseOperation1()
funcbaseOperation2()
funcbaseOperation3()
/// These operations have to be implemented in subclasses. funcrequiredOperations1() funcrequiredOperation2()
/// These are "hooks." Subclasses may override them, but it's not mandatory /// since the hooks already have default (but empty) implementation. Hooks /// provide additional extension points in some crucial places of the /// algorithm. funchook1() funchook2() }
/// These operations already have implementations. funcbaseOperation1() { print("AbstractProtocol says: I am doing the bulk of the work\n") }
funcbaseOperation2() { print("AbstractProtocol says: But I let subclasses override some operations\n") }
funcbaseOperation3() { print("AbstractProtocol says: But I am doing the bulk of the work anyway\n") }
funchook1() {} funchook2() {} }
/// Concrete classes have to implement all abstract operations of the base /// class. They can also override some operations with a default implementation. classConcreteClass1: AbstractProtocol {
/// The client code calls the template method to execute the algorithm. Client /// code does not have to know the concrete class of an object it works with, as /// long as it works with objects through the interface of their base class. classClient { // ... staticfuncclientCode(useobject: AbstractProtocol) { // ... object.templateMethod() // ... } // ... }
/// Let's see how it all works together. classTemplateMethodConceptual: XCTestCase {
functest() {
print("Same client code can work with different subclasses:\n") Client.clientCode(use: ConcreteClass1())
print("\nSame client code can work with different subclasses:\n") Client.clientCode(use: ConcreteClass2()) } }
/// A good example of Template Method is a life cycle of UIViewController
functestTemplateMethodReal() {
let accessors = [CameraAccessor(), MicrophoneAccessor(), PhotoLibraryAccessor()]
accessors.forEach { item in item.requestAccessIfNeeded({ status in let message = status ?"You have access to " : "You do not have access to " print(message + item.description +"\n") }) } } }
overridefuncrequestAccess(_completion: @escapingCompletion) { AVAudioSession.sharedInstance().requestRecordPermission { status in completion(status) } }
overridefuncdidReceiveAccess() { /// We want to track how many people give access to the PhotoLibrary. print("PhotoLibrary Accessor: Receive access. Updating analytics...") }
overridefuncdidRejectAccess() { /// ... and also we want to track how many people rejected access. print("PhotoLibrary Accessor: Rejected with access. Updating analytics...") } }
Output.txt: 执行结果
1 2 3 4 5 6
You have access to Camera
You have access to Microphone
PhotoLibrary Accessor: Rejected with access. Updating analytics... You do not have access to PhotoLibrary
/** * The Abstract Class defines a template method that contains a skeleton of some * algorithm, composed of calls to (usually) abstract primitive operations. * * Concrete subclasses should implement these operations, but leave the template * method itself intact. */ abstractclassAbstractClass { /** * The template method defines the skeleton of an algorithm. */ publictemplateMethod(): void { this.baseOperation1(); this.requiredOperations1(); this.baseOperation2(); this.hook1(); this.requiredOperation2(); this.baseOperation3(); this.hook2(); }
/** * These operations already have implementations. */ protectedbaseOperation1(): void { console.log('AbstractClass says: I am doing the bulk of the work'); }
protectedbaseOperation2(): void { console.log('AbstractClass says: But I let subclasses override some operations'); }
protectedbaseOperation3(): void { console.log('AbstractClass says: But I am doing the bulk of the work anyway'); }
/** * These operations have to be implemented in subclasses. */ protectedabstract requiredOperations1(): void;
protectedabstract requiredOperation2(): void;
/** * These are "hooks." Subclasses may override them, but it's not mandatory * since the hooks already have default (but empty) implementation. Hooks * provide additional extension points in some crucial places of the * algorithm. */ protectedhook1(): void { }
protectedhook2(): void { } }
/** * Concrete classes have to implement all abstract operations of the base class. * They can also override some operations with a default implementation. */ classConcreteClass1extendsAbstractClass { protected requiredOperations1(): void { console.log('ConcreteClass1 says: Implemented Operation1'); }
/** * The client code calls the template method to execute the algorithm. Client * code does not have to know the concrete class of an object it works with, as * long as it works with objects through the interface of their base class. */ functionclientCode(abstractClass: AbstractClass) { // ... abstractClass.templateMethod(); // ... }
console.log('Same client code can work with different subclasses:'); clientCode(newConcreteClass1()); console.log('');
console.log('Same client code can work with different subclasses:'); clientCode(newConcreteClass2());
Output.txt: 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass1 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass1 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses: AbstractClass says: I am doing the bulk of the work ConcreteClass2 says: Implemented Operation1 AbstractClass says: But I let subclasses override some operations ConcreteClass2 says: Overridden Hook1 ConcreteClass2 says: Implemented Operation2 AbstractClass says: But I am doing the bulk of the work anyway