2016-07-25 3 views
-1

мне нужно выполнить операцию на подкласс и возвращает результат:Могу ли я использовать дженерики для изменения подписи метода подкласса?

public interface IEntity { } 

public abstract class Entity : IEntity { 
    public abstract IEntity doStuff(IEntity e1, IEntity e2, IEntity e3); 
} 

public class Customer : Entity { 
    public override IEntity doStuff(IEntity e1, IEntity e2, IEntity e3) { // <-- yuck 
    var customer1 = e1 as Customer;           // <-- yuck 
    var customer2 = e2 as Customer;           // <-- yuck 
    var customer3 = e3 as Customer;           // <-- yuck 
    // do stuff 
    return foo; 
    } 
} 

Я хочу, чтобы избежать этого подкласса сигнатуры методы, и связанные с ним типажами.

Так что я предпочитаю это:

public override Customer doStuff(Customer c1, Customer c2, Customer c3) { 
    // do stuff 
    return foo; 
    } 

Как бы я использовать систему типов для достижения этой цели?

EDIT: Я изменил название метода от compare до doStuff, чтобы повторить, что сама операция не имеет значения. Я не пытаюсь сравнивать. Я просто хочу изменить типы. Как мне это сделать?


Многие говорили, изменить дизайн, но я не могу сделать. У меня уже есть решение, которое я включил ниже, и он работает. Если у вас есть лучший способ сделать это, не критикуя дизайн (это не главное), пожалуйста, дайте мне знать. Спасибо за вклад каждого.

+0

Я думаю, что важная часть отсутствует в вопросе * почему * вы хотите/должны сделать это таким образом, а не просто «публичный клиент ChooseBetter (Customer c1, Customer c2)» или обычный «IComparable » или ' IEquatable 'реализация. Что вы хотите, чтобы вызывающий абонент мог делать с этими классами? – 31eee384

+0

@ 31eee384 Если у меня есть дерево наследования, мне нужно начать с интерфейса или общего. Кроме того, как уже упоминалось выше, сама операция не является ядром проблемы, так как меняет сигнатуру, которая меня путает. –

+0

Что вы хотите делать, когда 'e1' или' e2' (или оба? Возможно, вам нужен только один параметр, и использовать 'this' для другого ...) являются * not *' Customer's? – 31eee384

ответ

2

Это работает. Но саморегуляторные дженерики заставляют мою голову болеть.

public interface IEntity { } 

public abstract class Entity<TEntity> : IEntity where TEntity : IEntity { 
    public abstract TEntity doStuff(TEntity e1, TEntity e2, TEntity e3); 
} 

public class Customer : Entity<Customer> { 
    public override Customer doStuff(Customer c1, Customer c2, Customer c3) { 
    // do stuff 
    return foo; 
    } 
} 

Является ли это хорошим решением, или там есть проблемы, которые могут вызвать проблемы?

Есть ли лучшее решение?

+0

Вы на правильном пути. Проверьте интерфейс IEquatable для стандартного способа реализации этого. – lintmouse

+0

Как я уже упоминал выше, я просто выбрал «сравнить» наугад (возможно, плохой выбор). Мне не нужно сравнивать само по себе. Я мог бы также использовать 'selectBetterCustomer()', 'selectHighestProfitAccount()' и т. Д. И т. Д. Реальная точка, которую я пытаюсь сделать, это изменить подпись ... –

+1

Хорошо, но эти методы звучат довольно специфично для типа сущность, так как бы вы реализовали их в абстрактном классе и принудительно их реализовать? – lintmouse

1

В перспективе незнания прочности, сериализации (JSON/XML) и бинаризации для сетевой передачи вам может быть лучше без интерфейса.

Чтобы отличить от классов POCO, которые также называются объектами, я предпочитаю первоначальный термин «объект домена», как указано в оригинальной работе Эрика Эванса.

Это всего лишь пример того, что я буду делать.

public abstract class AbstractDomainObject<T> 
    where T : AbstractDomainObject 
{ 
    public abstract bool CanDoStuffWith(T other); 
} 

public class Customer : AbstractDomainObject<Customer> 
{ 
    public override bool CanDoStuffWith(Customer other) 
    { 
     return this.Gender != other.Gender; 
    } 
} 
+0

Моя проблема не в сравнении, хотя ... –

+1

Если вы посмотрите внимательно, вы увидите, что мой ответ тоже не является. Возможность сравнения добавляется через общий интерфейс, в то время как объект домена остается абстрактным (и не добавляется к самому интерфейсу). Вы можете определить больше возможностей в качестве общих интерфейсов и при необходимости привязать их к дереву наследования. Это выше всего лишь пример для сравнения объектов, которые легко ослабить в других сценариях. – pid

+0

Хорошо, теперь я вижу. Но так, чтобы я правильно вас понял, ваше решение отличается от моего только тем, что вы избегали интерфейса? –

1

Причина, почему это встретив как сбивает с толку то, что это

public abstract class Entity : IEntity { 
    public abstract IEntity doStuff(IEntity e1, IEntity e2, IEntity e3); 
} 

не должны осуществлять IEntity. Если вам нужен класс, который «делает материал» с IEntity, то это отдельно от IEntity. Это принцип единой ответственности. Если основная цель IEntity не равна doStuff, то doStuff не будет находиться в одном классе.

Независимо от того, doStuffделает, вот что этот класс должен быть.Например, если он сравнивает, совместимы ли для некоторых операций экземпляры IEntity, вы можете сделать это:

public interface ICompatibilityChecker<TEntity> where TEntity : IEntity 
{ 
    bool AreCompatible(TEntity entity, TEntity other); 
} 

Тогда вы можете обнаружить, что вам даже не нужен абстрактный класс:

public class CompatibilityChecker : ICompatibilityChecker<Customer> 
{ 
    public bool AreCompatible(Customer entity, Customer other) 
    { 
     //check compatibility 
    } 
} 

Другой важный принцип: Favor Composition Over Inheritance. Есть тенденция хотеть поместить функциональность в базовые классы, когда часто имеет смысл вкладывать ее в совершенно отдельные классы. Но обратите внимание, что, когда вы выделяете некоторые из этих поведений на отдельные классы, дизайн этих классов становится намного проще.

Также, если у вас есть объекты домена, которые не нуждаются в этом поведении? Тогда наследование становится более сложным, потому что вам могут понадобиться другие методы в абстрактном классе, но вы застреваете в реализации другого, который вам даже не нужен.

Чаще всего, когда мы пытаемся разделить функциональность между классами, наследуя их от общих базовых классов, он становится запутанным и в конечном итоге ломается. Это намного легче поддерживать, если мы разделяем поведение и обязанности на отдельные классы.

+0

Спасибо, что нашли время, чтобы представить свои мысли. Я согласен с почти 100% того, что вы сказали. Однако в нашем случае это ограничение дизайна. Объекты моделируются после мода DDD и отвечают за их собственные операции. Ваш пример защищает класс обслуживания, который решает одну проблему, но создает других в нашем дизайне. Все, что я хотел знать, это «изменить» подпись, а не перепроектировать систему. Мое решение выше работает BTW, и хотя оно выглядит сложным, потребитель класса защищен от этой сложности. –

+0

Кроме того, проблема с StackOverflow заключается в том, что вам часто приходится опускать вашу настоящую проблему, которую вы не можете публиковать в Интернете. Поэтому, если сам пример плох, люди критикуют его, а не решают вопрос. Так что, может быть, мой пример не самый лучший! :-) Еще раз спасибо. –

Смежные вопросы