資産追跡アプリケーションを構築しています。SQL サーバー 2008、C# .NET、および Entity Framework を使用します。リポジトリを作成するのはこれが初めての経験です。私の調査によると、リポジトリはデータ アクセス プロセスを抽象化することを目的としています。私はいくつかのデザインを試してきましたが、それらのどれが優れているか、または重大な開発リスクをもたらすかどうかに興味があります. リポジトリは、シリアル番号、バーコード、またはホスト名によるアセットのクエリをサポートする必要があります。
例を簡潔にするために、update、delete、insert メソッドは含めません。また、一般的な実装は後でいつでも解決できる可能性があり、現時点では例をより混乱させるだけであるため、例では可能な一般的な実装を無視しています。次の 3 つのデザインを読んで、正しい軌道に乗っているかどうかをお知らせください。
デザイン1
私の最初のデザインは次のようになりました。
public interface IAssetRepository
{
public Asset fetchBySerialNumber(String serialNumber);
public Asset fetchByBarcode(String barcode);
public ICollection<Asset> fetchByHostname(String hostname);
public Asset fetchActiveByHostname(String hostname);
}
public class AssetRepository : IAssetRepository
{
private InventoryEntites entities;
public Asset fetchBySerialNumber(String serialNumber)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.SerialNumber == serialNumber
select a;
return query.FirstOrDefault();
}
public Asset fetchByBarcode(String barcode)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Barcode == barcode
select a;
return query.FirstOrDefault();
}
public Asset fetchActiveByHostname(String hostname)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Hostname == hostname && a.IsDeployed == true
select a;
return query.FirstOrDefault();
}
public ICollection<Asset> fetchByHostname(String hostname)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Hostname == hostname
select a;
return query.ToList();
}
}
デザイン 2
2 回目の試行では、パラメーターとして使用されるプリミティブ型をラップすることで、アドホック ポリモーフィズムを利用できると考えました。
public interface IAssetRepository
{
public Asset fetch(SerialNumber serialNumber);
public Asset fetch(Barcode barcode);
public ICollection<Asset> fetch(Hostname hostname);
public Asset fetchActive(Hostname hostname);
}
public class SerialNumber
{
private String value;
public SerialNumber(String value)
{ this.value = value; }
public String Value
{
get { return this.value; }
}
}
// Barcode and Hostname classes are similar to SerialNumber
public class AssetRepository : IAssetRepository
{
private InventoryEntites entities;
public Asset fetch(SerialNumber serialNumber)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.SerialNumber == serialNumber.Value
select a;
return query.FirstOrDefault();
}
public Asset fetch(Barcode barcode)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Barcode == barcode.Value
select a;
return query.FirstOrDefault();
}
public ICollection<Asset> fetch(Hostname hostname)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Hostname == hostname.Value && a.IsDeployed == true
select a;
return query.FirstOrDefault();
}
public Asset fetchActive(Hostname hostname)
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Hostname == hostname.Value
select a;
return query.ToList();
}
}
デザイン3
「教えて、聞くな」の精神で、私の最後の設計では、値を尋ねる代わりに、実際のクエリを SerialNumber、Hostname、および Barcode クラスに移動します。SerialNumber などのクラスには、データ ソースへの参照が含まれている必要があります。これらはおそらく、さまざまなデータ ソースをサポートできるようにインターフェイスにすることでメリットが得られます。それぞれがエンティティへの個別の参照を持っているため、これが良い設計であるかどうかはわかりません。クライアントは、リポジトリにオブジェクトを送信する前に、オブジェクト (SerialNumber など) を構築する必要があります。クライアントはエンティティへの参照を持たないため、構築中にエンティティへの同じ参照を注入することはできません。
// Same interface as last
public interface IAssetRepository
{
public Asset fetch(SerialNumber serialNumber);
public Asset fetch(Barcode barcode);
public ICollection<Asset> fetch(Hostname hostname);
public Asset fetchActive(Hostname hostname);
}
// Could include other methods like, findStartingWith(), findContains(), etc.
public class SerialNumber
{
private InventoryEntites entities;
private String value;
public SerialNumber(String value)
{
this.value = value;
this.entities = new InventoryEntities();
}
public Asset find()
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.SerialNumber == this.value
select a;
return query.FirstOrDefault();
}
}
// Barcode classes is similar to SerialNumber
public class Hostname
{
private InventoryEntites entities;
private String value;
public Hostname(String value)
{
this.value = value;
this.entities = new InventoryEntities();
}
public ICollection<Asset> find()
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Hostname == this.value
select a;
return query.ToList();
}
public Asset findActive()
{
IQueryable<Asset> query = from a in this.entities.Assets
where a.Hostname == this.value && a.IsDeployed == true
select a;
return query.FirstOrDefault();
}
}
public class AssetRepository : IAssetRepository
{
private InventoryEntites entities;
public Asset fetch(SerialNumber serialNumber)
{
return serialNumber.find();
}
public Asset fetch(Barcode barcode)
{
return barcode.find();
}
public ICollection<Asset> fetch(Hostname hostname)
{
return hostname.find();
}
public Asset fetchActive(Hostname hostname)
{
return hostname.findActive();
}
// Other methods could include
public ICollection<Asset> fetch(Location location)
{
return location.find();
}
public ICollection<Asset> fetchActive(Location location)
{
return location.findActive();
}
}
アップデート
いくつかの調査を行って、この記事を見つけました:
この引用は私に設計 3 を思い起こさせました。おそらく、この小さなクラスのグループは、設計 2 のような設計にまとめるべきでしょうか?
Shotgun Surgeryシステムの特定のタイプの変更が繰り返されると、クラスのグループに多くの小さな変更が加えられます。散弾銃手術は、一般に、単一の論理的なアイデアまたは機能が複数のクラスに分散していることを意味します。一緒に変更する必要があるコードのすべての部分を単一のまとまりのあるクラスに引っ張って、これを修正してみてください。