Załóżmy, że mam różne podmioty w moim modelu (przy użyciu EF), powiedzmy Użytkownik, Produkt, Faktura i Zamówienie.
Piszę kontrolkę użytkownika, która może wydrukować podsumowania obiektów encji w mojej aplikacji, gdzie encje należą do wcześniej ustalonego zestawu, w tym przypadku mówię, że streszczenia użytkownika i produktu można podsumować.
Wszystkie streszczenia będą miały tylko identyfikator i opis, dlatego tworzę prosty interfejs:
public interface ISummarizableEntity {
public string ID { get; }
public string Description { get; }
}
Następnie dla przedmiotowych encji tworzę klasę częściową, która implementuje ten interfejs:
public partial class User : ISummarizableEntity
{
public string ID
{
get{ return UserID.ToString(); }
}
public string Description
{
get{ return String.Format("{0} {1} is from {2} and is {3} years old", FirstName, LastName, Country, Age); }
}
}
public partial class Product: ISummarizableEntity
{
public string ID
{
get{ return ProductID.ToString(); }
}
public string Description
{
get{ return String.Format("{0} weighs {1}{2} and belongs in the {3} department", ProductName, WeightValue, WeightUnit, Department); }
}
}
W ten sposób moja kontrola użytkownika / częściowy widok może po prostu powiązać się z dowolną kolekcją ISummarizableEntity i nie musi być wcale zainteresowany źródłem. Powiedziano mi, że interfejsy nie powinny być używane jako typy danych, ale nie otrzymałem więcej informacji. Z tego co widzę, chociaż interfejsy zwykle opisują zachowanie, samo użycie właściwości nie jest samo w sobie anty-wzorem, ponieważ właściwości są po prostu cukrem syntaktycznym dla pobierających / ustawiających.
Mógłbym stworzyć konkretny typ danych i mapę z encji do tego, ale nie widzę korzyści. Mógłbym sprawić, aby obiekty encji dziedziczyły po klasie abstrakcyjnej, a następnie zdefiniować właściwości, ale następnie blokuję encje, aby nie mogły być dalej używane, ponieważ nie możemy mieć wielokrotnego dziedziczenia. Jestem również otwarty na to, że dowolny obiekt jest ISummarizableEntity, gdybym chciał (oczywiście zmieniłbym nazwę interfejsu)
Rozwiązanie, którego używam w umyśle, jest łatwe do utrzymania, rozszerzalne, testowalne i dość solidne. Widzisz tutaj anty-wzór?
EntitySummary
,User
aProduct
każdy ma taką metodępublic EntitySummary GetSummary()
?Odpowiedzi:
Interfejsy nie opisują zachowania. Czasami wręcz przeciwnie.
Interfejsy opisują umowy, takie jak „jeśli mam zaoferować ten obiekt dowolnej metodzie, która akceptuje ISummarizableEntity, ten obiekt musi być bytem, który jest w stanie się streścić” - w twoim przypadku jest to zdefiniowane jako możliwość zwrotu identyfikator ciągu i ciąg Opis.
To idealne wykorzystanie interfejsów. Tutaj nie ma anty-wzoru.
źródło
List
listą, która nie zachowuje się jak lista?Wybrałeś lepszą ścieżkę dla tego projektu, ponieważ definiujesz określony typ zachowania, które będzie wymagane od wielu różnych typów obiektów. Dziedziczenie w tym przypadku oznaczałoby wspólny związek między klasami, który tak naprawdę nie istnieje. W takim przypadku kompozycyjność jest ważniejsza niż dziedziczenie.
źródło
Należy unikać interfejsów posiadających tylko właściwości, ponieważ:
Mieszacie dwie obawy:
Podsumowanie składa się z dwóch ciągów: identyfikatora i opisu. To są zwykłe dane:
Po zdefiniowaniu podsumowania chcesz zdefiniować umowę:
Zauważ, że używanie inteligencji w getterach jest anty-wzorcem i należy tego unikać: zamiast tego powinno się znajdować w funkcjach. Oto jak wyglądają wdrożenia:
źródło