TL;DR: SOLID の原則に違反せずにインターフェイス間でデータを移動する最善の方法は何ですか?
私はこれを考えすぎているかもしれませんが、SOLID の原則に関して独断的になるつもりはありません。しかし、私はいくつかのインプットを得たかったのです。私はショッピングカートをより「しっかり」するようにリファクタリングしてきましたが、私が書いたメソッドは「コードの匂い」のように思えます (そうではないかもしれません)。
次のようなCartHelperクラスがあります (簡潔にするために少し簡略化しています)。
パブリック クラス CartHelper
{
IEnumerable Products;
IEnumerable サブスクリプション。
// ...その他のクラスメソッド...
[HttpPost]
public void AddItem(int productVariantID)
{
var product = ProductService.GetByVariantID(productVariantID);
if (製品 != null)
{
if (product.Type == (int)Constants.ProductTypes.Subscription)
Subscriptions = Subscriptions.Concat(new [] { product });
Products = Products.Concat(new [] { product });
CartService.AddItem(productVariantID);
}
}
[HttpPost]
public void RemoveItem(int productVariantID)
{
サブスクリプション = Subscriptions.Where(s => s.VariantID != productVariantID);
Products = Products.Where(p => p.VariantID != productVariantID);
CartService.RemoveItem(productVariantID);
}
公開小数 GetCartTotalBeforeDiscount()
{
Products.Sum(p => p.Price) を返します。
}
public IEnumerable GetCartItems()
{
var products = (Products の p から
新しい CartSummaryItem を選択
{
ProductID = p.ProductID、
タイトル = p.タイトル、
説明 = p.説明、
価格 = p.価格、
// ...他の適用可能なプロパティをここに割り当てます...
}
ICartSummaryItem として);
製品を返品します。
}
// ...その他のクラスメソッド...
}
私にとって「コード臭」のように見える部分 (そして、ここにはもっと悪い部分があるかもしれません) はGetCartItems()メソッドです。それについての何かが私にはファンキーに思えますが、それ以上の代替案は思いつきません.
ビューに渡す必要があるいくつかのプロパティが追加されていますが、または(インターフェイス分離の原則) ではICartItem意味がありません。IStoreProductIStoreSubscription
ConvertProductToCartItem()にとConvertSubscriptionToCartItem()メソッドを追加することを考えましたCartHelperが、それは単一責任の原則に違反しているようです。IStoreProducts とsを受け入れて変換する CartItemFactory を持つことは理にかなっていIStoreSubscriptionますか? このような単純な変換には、多くの不必要なオーバーヘッドがかかるようです。
私が思いついた解決策の 1 つは、明示的なキャスト メソッドを定義することです。
パブリック クラス StoreProduct : IStoreProduct
{
公開小数価格{取得; 設定; }
public decimal 割引 { get; 設定; }
// ...プロパティ...
public ICartItem ToCartItem()
{
// 明示的なキャスト実装を呼び出します
return (CartItem) this;
}
// Product を CartItem として明示的にキャスト変換する
public static explicit operator CartItem(StoreProduct 商品)
{
new CartItem() を返す
{
価格 = 商品.価格、
割引 = 商品.価格、
SalePrice = Helper.CalculateSalePriceForProduct(製品)、
// ...他の適用可能なプロパティをここに割り当てます...
};
}
}
これにより、GetCartItemsメソッドをこのよりクリーンな実装に変更できます。
public IEnumerable GetCartItems()
{
製品を返します。選択 (p => p.ToCartSummaryItem());
}
しかし、このアプローチの問題は、クラスへのカップリングICartItemによって単一責任の原則にも違反することです。キャスト変換の代わりに拡張メソッドも検討しましたが、それは優れているわけでも違いがあるわけでもありません。CartItemStoreProduct
具象StoreProductクラスにインターフェイスを実装させ、ICartItemそこにカート固有のプロパティを配置する必要がありますか? CartHelperICartItems のみを持つように (つまり、s を削除して) を書き直すべきIProductでしょうか? これらのオプションは両方とも、単一責任の原則に違反しているように見えます。たぶん、私が少し寝た後に解決策が明らかになるでしょう...
要するに、私の質問は、SOLID の原則に違反することなくインターフェース間でデータを移動する最善の方法は何かということだと思います。
助言がありますか?たぶん、私はそれについて心配する必要はありません (つまり、SOLID について独断的にならないでください)。私は自分の質問に答えましたか?これはプログラマー.stackexchange に属している可能性があります。主観的すぎないことを願っています。
また、役に立つ場合は、これが私のインターフェイスの外観です。
パブリック インターフェイス IProductBase
{
int ProductID { get; 設定; }
10 進価格 { get; 設定; }
文字列のタイトル { get; 設定; }
文字列 説明 { get; 設定; }
// ... その他のプロパティ...
}
パブリック インターフェイス IStoreProduct : IProductBase
{
int バリアント ID { get; 設定; }
小数の割引{取得; 設定; }
// ... その他のプロパティ...
ICartItem ToCartItem();
}
パブリック インターフェイス ISubscription : IProductBase
{
SubscriptionType SubscriptionType { get; 設定; }
// ... その他のプロパティ...
ICartItem ToCartItem();
}
パブリック インターフェイス ICartItem : IProductBase
{
10進数のSalePrice {get; 設定; }
// ... その他のプロパティ...
}
更新:わかりやすくするために投稿属性を追加しました。