さて、私はMSDN の「基本クラスの使用法」に関する優れた記事を読んでいました。基本クラスとインターフェイスの概念は理解できますが、この記事の 2 番目の段落 (「保護されたメソッドとコンストラクター」) のテンプレート メソッドの使用法を理解できません。
簡単な実際の例を使って、この概念を理解するのを手伝ってくれる人はいますか? おそらく、テンプレート メソッドの概念を理解することから始めるのがよいでしょう。
前もって感謝します。
さて、私はMSDN の「基本クラスの使用法」に関する優れた記事を読んでいました。基本クラスとインターフェイスの概念は理解できますが、この記事の 2 番目の段落 (「保護されたメソッドとコンストラクター」) のテンプレート メソッドの使用法を理解できません。
簡単な実際の例を使って、この概念を理解するのを手伝ってくれる人はいますか? おそらく、テンプレート メソッドの概念を理解することから始めるのがよいでしょう。
前もって感謝します。
私の意見では、これは非常に古い記事であり、Impl での命名を見たことを覚えていません。
ウィキペディアの方が説明が適切だと思います:
テンプレート メソッドは次の目的で使用されます。
サブクラスに (メソッドのオーバーライドを通じて) 変更可能な動作を実装させる
コードの重複を避ける: 一般的なワークフロー構造は抽象クラスのアルゴリズムで 1 回実装され、必要なバリエーションは各サブクラスで実装されます。
サブクラス化が許可されるポイントを制御します。単純なポリモーフィック オーバーライドとは対照的に、ベース メソッドを完全に書き直してワークフローを根本的に変更するのではなく、ワークフローの特定の詳細のみを変更できます。
テンプレート パターンを適用した結果である制御構造 (制御の反転) は、ハリウッドの原則と呼ばれることがよくあります。この原則を使用して、親クラスのテンプレート メソッドは、必要に応じてサブクラスのメソッドを呼び出して、プロセス全体を制御します。
簡単に言えば、基本クラスでスケルトンを定義し、派生クラスは実装間の違いを実装します。
さまざまなチャネルに公開する必要がある情報があるとします。
そのため、基本クラス Publisher を作成します。これには、それを行う方法のスケルトンがあります。
すべての派生が公開先のアドレスを設定するように、初期化を実装することを強制します。
ほとんどのチャネルに適合する送信実装を作成し、一部のチャネルが http の代わりに ftp を使用する場合、送信をオーバーライドできます。
そして、行われたことをデータベースに記録することは、すべてのチャネルで同じであるため、それをオーバーライドすることはできません。
Publisher 派生クラスのユーザーにとって興味深いのは公開のみであるため、そのメソッドのみが公開されます。
public abstract class Publisher
{
private address;
// if you wish to force implementation in derived class, make method abstract
private abstract void Initialize();
// if you wish optional implementation in derived class, make it virtual
protected virtual void SendChangesToWeb()
{
// ...
webClient.Upload(address, data)
}
// if you wish that some step could not be changed from outside
private void LogSentChangesToDatabase()
{
// ... save date time when was send and what was sent
}
// this sequence is the same for all derives, no point to duplicate
public void PublishUpdates()
{
Initialize();
SendChangesToWeb();
LogSentChangesToDatabase();
}
}
public class GooglePublisher : Publisher {
private override Initialize()
{
address = "http://www.google.com";
}
}
public class FtpPublisher : Publisher {
private override Initialize()
{
address = "ftp://test.com";
}
protected override SendChangesToWeb()
{
FtpClient.Upload(address, data)
}
}
アイデアは、すべてが単一のメソッドを内部的に使用するメソッドの複数のパブリック オーバーロードがあるということです。したがって、公開されているオーバーロードには実装自体がありません。代わりに、すべてのオーバーロードの実際の実装に保護されたメソッドが使用されます。
したがって、まず第一に、実装は 1 回しかなく、デフォルトのオーバーロードはすべて、いくつかのデフォルト値を設定して実装を呼び出すだけなので、繰り返す必要はありません。
クラスを継承するとき、派生クラスは内部実装を一度オーバーライドするだけで、以前に公開されていたすべてのオーバーロードがすぐに新しい実装を使用するようになります。したがって、標準実装を使用して基本クラスでパブリック インターフェイスを指定できますが、派生クラスはインターフェイス コントラクトを順守しながらその実装を変更できます。
実装が別のメソッドに入れられた理由を議論することができますが、私には真剣にわかりません。代わりに、メソッドの最も一般的なシグネチャを簡単に実装し、他のメソッドが内部のシグネチャの代わりにそのシグネチャを呼び出せるようにすることができます。別のメソッドの理由は、パブリック メソッドには表示されない内部使用パラメーターを追加できるためかもしれませんが、それは何をしたいかによって異なると思います。
Template Method Design Pattern を検索できます。このパターンには、一連のメソッドを呼び出すスケルトンを提供する Template メソッドが含まれています。全体的な呼び出しシーケンスを変更せずに、1 つまたは複数のステップをこれらのステップを実装するサブクラスに延期できます。例:
// Template Method pattern -- Structural example
using System;
namespace DoFactory.GangOfFour.Template.Structural
{
/// <summary>
/// MainApp startup class for Real-World
/// Template Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
AbstractClass aA = new ConcreteClassA();
aA.TemplateMethod();
AbstractClass aB = new ConcreteClassB();
aB.TemplateMethod();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'AbstractClass' abstract class
/// </summary>
abstract class AbstractClass
{
public abstract void PrimitiveOperation1();
public abstract void PrimitiveOperation2();
// The "Template method"
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
Console.WriteLine("");
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
class ConcreteClassA : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation1()");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation2()");
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
class ConcreteClassB : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClassB.PrimitiveOperation1()");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClassB.PrimitiveOperation2()");
}
}
}