私が変換に近づく方法は、システムを永久に状態を変更する部分を見ることです。ファイル、データベース、外部コンテンツ。一度変更して再読み込みしたら、それは良い状態に変わったのですか?これは、これを変更するための最初の場所です。
class MyXmlFileWriter
{
public bool WriteData(string fileName, string xmlText)
{
// TODO: Sort out exception handling
try
{
File.WriteAllText(fileName, xmlText);
return true;
}
catch(Exception ex)
{
return false;
}
}
}
は、第二に、あなたがリファクタリング中に、コードを壊していないことを確認するためにユニットテストを書く:
だからあなたが最初にすることは、このようなソースを変更する場所を見つけることです。
[TestClass]
class MyXmlWriterTests
{
[TestMethod]
public void WriteData_WithValidFileAndContent_ExpectTrue()
{
var target = new MyXmlFileWriter();
var filePath = Path.GetTempFile();
target.WriteData(filePath, "<Xml/>");
Assert.IsTrue(File.Exists(filePath));
}
// TODO: Check other cases
}
次に、元のクラスからインターフェイスを抽出します。
interface IFileWriter
{
bool WriteData(string location, string content);
}
class MyXmlFileWriter : IFileWriter
{
/* As before */
}
は、テストを再実行し、すべてが良いです願っています。元のテストは、以前の実装が動作しているかどうかを確認したままにしておきます。
次に、何もしない偽の実装を作成します。ここでは非常に基本的な振る舞いしか実装しません。それユニットテストやリファクタリングのバージョンと
// Put this class in the test suite, not the main project
class FakeFileWriter : IFileWriter
{
internal bool WriteDataCalled { get; private set; }
public bool WriteData(string file, string content)
{
this.WriteDataCalled = true;
return true;
}
}
そして、ユニットテスト、それ...
今
class FakeFileWriterTests
{
private IFileWriter writer;
[TestInitialize()]
public void Initialize()
{
writer = new FakeFileWriter();
}
[TestMethod]
public void WriteData_WhenCalled_ExpectSuccess()
{
writer.WriteData(null,null);
Assert.IsTrue(writer.WriteDataCalled);
}
}
はまだ働いて、私たちは、注射した場合、呼び出し元のクラスがインタフェースを使用していることを確認する必要はありません具体的なバージョン!
// Before
class FileRepository
{
public FileRepository() { }
public void Save(string content, string xml)
{
var writer = new MyXmlFileWriter();
writer.WriteData(content,xml);
}
}
// After
class FileRepository
{
private IFileWriter writer = null;
public FileRepository() : this(new MyXmlFileWriter()){ }
public FileRepository(IFileWriter writer)
{
this.writer = writer;
}
public void Save(string path, string xml)
{
this.writer.WriteData(path, xml);
}
}
私たちは何をしましたか?
- ノーマルタイプ
- を使用して、既定のコンストラクタを持っては
IFileWriter
タイプ
- が参照されたオブジェクトを保持するために、インスタンスフィールドを使用しとるコンストラクタを持っています。
そして、そのFileRepository
ためのユニットテストを書いて、メソッドが呼び出されたことを確認した場合:
[TestClass]
class FileRepositoryTests
{
private FileRepository repository = null;
[TestInitialize()]
public void Initialize()
{
this.repository = new FileRepository(new FakeFileWriter());
}
[TestMethod]
public void WriteData_WhenCalled_ExpectSuccess()
{
// Arrange
var target = repository;
// Act
var actual = repository.Save(null,null);
// Assert
Assert.IsTrue(actual);
}
}
がわかりましたが、ここでは、私たちは本当にFileRepository
またはFakeFileWriter
をテストしていますか?他のテストでFakeFileWriter
をテストしているので、FileRepository
をテストしています。このクラス-FileRepositoryTests
は、入力パラメータのnullをテストする方が便利です。
偽物は何も巧妙ではありません - パラメータの検証もI/Oもありません。 FileRepositoryがコンテンツを任意の作業で保存できるようにするだけです。その目的は2つあります。ユニットテストを大幅にスピードアップし、システムの状態を破壊しないようにする。
このFileRepositoryも同様にファイルを読み込む必要がある場合は、IFileReaderを実装することもできます(極端なビットマップ)。最後に書き込まれたfilePath/xmlをメモリ内の文字列に格納して代わりに取得できます。
このように、どのようにこれにアプローチしますか?
多くのリファクタリングが必要な大規模なプロジェクトでは、単体テストをDI変更を行うクラスに組み込むことが常にベストです。理論的には、あなたのデータは数百の場所(コード内)にコミットされるべきではありませんが、いくつかの重要な場所に押し出されます。コード内でそれらを探し、それらのためのインターフェイスを追加します。非常に一般的な方法でデータソースから取得するために、あなたが設定されます
interface IReadOnlyRepository<TKey, TValue>
{
TValue Retrieve(TKey key);
}
interface IRepository<TKey, TValue> : IReadOnlyRepository<TKey, TValue>
{
void Create(TKey key, TValue value);
void Update(TKey key, TValue);
void Delete(TKey key);
}
:私が使用した1つのトリックは、各DBまたはこのようなインターフェイスの背後にあるインデックスのようなソースを非表示にすることです。 XmlRepository
からDbRepository
に切り替えるには、注入先を置き換えるだけです。これは、システムの内部に影響を与えることなく、あるデータソースから別のデータソースに移行するプロジェクトに非常に役立ちます。オブジェクトを使用するようにXML操作を簡単に変更することはできますが、この方法では新しい機能を維持し実装する方がはるかに簡単です。
私が与えることができる唯一の他のアドバイスは、一度に1つのデータソースです。一度に多くのことをする誘惑に抵抗する。実際にファイル、DB、Webサービスを一度に保存しなければならない場合は、Extract Interfaceを使用し、呼び出しを偽装して何も返しません。一度にたくさんのことをするのは本当のジャグリングですが、最初の原則から始めるよりも簡単に戻すことができます。
幸運を祈る!
リファクタリングは、手元の作業に必要なコードの小さなサブセットのみから開始することができます。そうすれば、あなたとあなたのチームはDIの感触を得て、DIの経験を集めるでしょう。 DIが動機づけをするデカップリングされたアーキテクチャは非常にテスト容易なので、単体テストを使用して何も破壊しないようにすることができます。 – lasseeskildsen
この質問はおそらくhttp://programmers.stackexchange.comに適しています –
これのためのツールはありません。それぞれのクラスをどのように分析してどの依存関係を抽出するかを考えてみましょう。ツールは信頼できる方法でこの分析を行うことはできません(あまりにも多くの情報が抽出されます)。ただし、抽出メソッドと抽出クラスのリファクタリングを手助けするツール(ResharperやCode Rushなど)がありますが、これはその時点では1つのクラスにしか適用されません。プロジェクト全体をワンクリックすることはありません。 – Steven