1

優れたオブジェクト指向設計を実際に体験したいので、関心の分離をレガシーアプリに適用することにしました。

私は、これらの呼び出しがコードベース全体に散らばっているのは気が進まないと判断しました。

ConfigurationManager.AppSettings["key"]

これらの呼び出しを静的メソッドにカプセル化するヘルパークラスを作成することでこれに取り組んだことがありますが、もう少し先に進む機会になると思いました。

最終的には、依存性注入の使用を目指し、常に「インターフェースへのコーディング」を行う必要があることを認識しています。しかし、私はあまりにも大きな一歩を踏み出したくありません。それまでの間、私はその究極の目標に向けて小さな一歩を踏み出したいと思います。

誰かが彼らが推奨するステップを列挙できますか?

頭に浮かぶものは次のとおりです。

  • クライアントコードを具体的な実装ではなくインターフェイスに依存させる

  • コンストラクターまたはプロパティを介してインターフェースに依存性を手動で注入しますか?

  • IoCコンテナを選択して適用する前に、コードを実行し続けるにはどうすればよいですか?

  • 依存関係を満たすために、構成値を必要とするクラスのデフォルトコンストラクターは、(静的CreateObject()メソッドを使用して)Factoryを使用できますか?

確かに私はまだファクトリーに具体的な依存関係を持っていますか?...

Michael Feathersの本に浸ったので、継ぎ目を導入する必要があることはわかっていますが、十分な数または多すぎる数を導入した時期を知るのに苦労しています。

アップデート

クライアントがWidgetLoaderのメソッドを呼び出して、必要な依存関係(IConfigReaderなど)を渡すと想像してください。

WidgetLoaderは、構成を読み取ってロードするウィジェットを見つけ、WidgetFactoryにそれぞれを順番に作成するように要求します。

WidgetFactoryはconfigを読み取り、ウィジェットをデフォルトでどの状態にするかを認識します

WidgetFactoryはWidgetRepositoryに委任してデータアクセスを行います。WidgetRepositoryはconfigを読み取り、ログに記録する診断を決定します。

上記のいずれの場合も、IConfigReaderは、コールチェーンの各メンバー間でホットポテトのように渡される必要がありますか?

工場は答えですか?

以下のコメントを明確にするために:

私の主な目的は、いくつかのアプリ設定を構成ファイルから他の形式の永続性に徐々に移行することです。注入された依存関係を使用すると、抽出してオーバーライドして単体テストの良さを得ることができますが、私の主な関心事は、設定が実際に永続化される場所を認識し始めるのに十分なカプセル化を行うことではありません。

4

3 に答える 3

3

レガシーコードベースをリファクタリングするときは、時間をかけて小さな変更を繰り返し行います。これが1つのアプローチです:

  • アプリの設定を取得するメソッド(つまり、GetAppSettingString(string key))を使用して、新しい静的クラス(つまり、MyConfigManager)を作成します。

  • グローバル検索を実行して「ConfigurationManager.AppSettings["key"]を置き換え、インスタンスを「MyConfigManager.GetAppSettingsString( "key")」に置き換えます

  • テストとチェックイン

これで、ConfigurationManagerへの依存関係が1か所になりました。大量のコードを変更することなく、データベースなどのどこにでも設定を保存できます。欠点は、静的な依存関係がまだあることです。

次のステップは、MyConfigManagerを通常のインスタンスクラスに変更し、それが使用されるクラスに挿入することです。ここでの最善のアプローチは、段階的に行うことです。

  • 静的クラスと一緒にインスタンスクラス(およびインターフェイス)を作成します。

  • 両方が用意できたので、すべてがインスタンスクラスを使用するまで、使用クラスをゆっくりとリファクタリングできます。インスタンスをコンストラクターに挿入します(インターフェースを使用)。使用法が多い場合は、ビッグバンチェックインを試みないでください。時間をかけてゆっくりと慎重に行ってください。

  • 次に、静的クラスを削除します。

于 2009-12-11T15:34:45.503 に答える
1

通常、レガシーアプリケーションをクリーンアップすることは、このように変更されるように設計されていないため、小さな手順です。コードが完全に混ざり合っていて、SoCがない場合、他のすべてを強制的に変更せずに変更することは困難です...また、何かを単体テストすることは非常に難しいことがよくあります。

ただし、一般的には、次のことを行う必要があります。1)まだリファクタリングされていない最も単純な(最小の)クラスを見つける2)リファクタリングによって何も壊れていないことを確信できるように、このクラスの単体テストを作成する3)可能な限り最小の変更を行う(これはプロジェクトとあなたの常識について)4)すべてのテストに合格していることを確認します5)コミットして1に進みます

より多くのアイデアを提供するために、Martin Fowlerによる「リファクタリング」をお勧めします:http://www.amazon.com/exec/obidos/ASIN/0201485672

于 2009-12-11T15:28:18.833 に答える
1

あなたの例では、私が最初にすることは、設定を読むために必要な機能を公開するインターフェースを作成することです。

public interface IConfigReader
{
    string GetAppSetting(string key);
    ...
}

次に、静的ConfigurationManagerクラスに委任する実装を作成します。

public class StaticConfigReader : IConfigReader
{
    public string Get(string key)
    {
        return ConfigurationManager.AppSetting[key];
    }
}

次に、構成に依存する特定のクラスに対して、最初は静的構成リーダーのインスタンスを返すだけのシームを作成できます。

public class ClassRequiringConfig
{
    public void MethodUsingConfig()
    {
        string setting = this.GetConfigReader().GetAppSetting("key");
    }
    protected virtual IConfigReader GetConfigReader()
    {
        return new StaticConfigReader();
    }
}

そして、ConfigManagerへのすべての参照をインターフェイスの使用法に置き換えます。次に、テストの目的で、このクラスをサブクラス化し、GetConfigReaderメソッドをオーバーライドして偽物を挿入し、実際の構成ファイルを必要としないようにします。

public class TestClassRequiringConfig : ClassRequiringConfig
{
    public IConfigReader ConfigReader { get; set; }
    protected override IConfigReader GetConfigReader()
    {
        return this.ConfigReader;
    }
}

[Test]
public void TestMethodUsingConfig()
{
    ClassRequiringConfig sut = new TestClassRequiringConfig { ConfigReader = fakeConfigReader };
    sut.MethodUsingConfig();

    //Assertions
}

その後、最終的には、IoCコンテナーを追加するときに、これをプロパティ/コンストラクターインジェクションに置き換えることができます。

編集:このような個々のクラスにインスタンスを挿入することに満足できない場合(多くのクラスが構成に依存している場合は非常に面倒です)、静的構成クラスを作成してから、テストのために構成リーダーに一時的な変更を加えることができます:

    public static class Configuration
{
    private static Func<IConfigReader> _configReaderFunc = () => new StaticConfigReader;

    public static Func<IConfigReader> GetConfiguration
    {
        get { return _configReaderFunc; }
    }

    public static IDisposable CreateConfigScope(IConfigReader reader)
    {
        return new ConfigReaderScope(() => reader);
    }

    private class ConfigReaderScope : IDisposable
    {
        private readonly Func<IConfigReader> _oldReaderFunc;

        public ConfigReaderScope(Func<IConfigReader> newReaderFunc)
        {
            this._oldReaderFunc = _configReaderFunc;
            _configReaderFunc = newReaderFunc;
        }

        public void Dispose()
        {
            _configReaderFunc = this._oldReaderFunc;
        }
    }
}

次に、クラスは静的クラスを介して構成にアクセスします。

public void MethodUsingConfig()
{
    string value = Configuration.GetConfigReader().GetAppSetting("key");
}

そして、あなたのテストは一時的なスコープを通して偽物を使うことができます:

[Test]
public void TestMethodUsingConfig()
{
    using(var scope = Configuration.CreateConfigScope(fakeReader))
    {
        new ClassUsingConfig().MethodUsingConfig();
        //Assertions
    }
}
于 2009-12-11T15:49:46.550 に答える