Обеспечение согласованности именования методов в C# между классами

У меня есть серия классов в проекте, которые делают по сути одно и то же, но на разных объектах. Из-за того, что некоторые из них были закодированы в разное время и разными людьми, в названиях есть некоторая непоследовательность. Я хочу обновить свой код таким образом, чтобы обеспечить некоторую согласованность не только в текущих объектах, но и в новых, которые будут созданы в будущем. Мое понимание программирования наводит меня на мысль, что мне нужен либо базовый класс, либо интерфейс, но я не могу понять, как заставить их работать. Методы, которые я хочу иметь:

internal BusinessObject MethodA(EntityObject entity)
internal Void MethodB(EntityContext context, BusinessObject obj, EntityObject entity)

Проблема, с которой я сталкиваюсь, заключается в том, что в каждом классе «BusinessObject» и «EntityObject» будут разными, например. в одном это может быть "CarObject и CarEntity", а в другом - "BusObject и BusEntity". Мне все еще нужны два метода, и я все еще хочу, чтобы они назывались MethodA и MethodB. Я просто хочу поменять местами фактические типы объектов в реализации. Сами реализации будут разными, потому что они используют разные объекты.

Я знаю типы объектов во время компиляции и должен иметь доступ к свойствам объектов, поэтому, если используются дженерики, мне нужно привести дженерик как правильный тип в реализации. Также реализация MethodA требует создания «нового» BusinessObject, т.е. «BusinessObject x = new BusinessObject()», если это имеет значение.

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

Каков наилучший способ справиться с этим? Пример кода будет высоко оценен.


person William    schedule 27.01.2011    source источник
comment
Не могли бы вы предоставить пример кода того, что вы пробовали? В частности, попытки дженериков.   -  person Anon.    schedule 27.01.2011


Ответы (3)


Дженерики — это путь

Поэтому я бы объявил интерфейс (или базовый класс, если вы предпочитаете этот маршрут) в следующих строках:

internal interface IEntityContext<TEntity>
{
 ???
}

internal interface IMyInterfaceName<TEntity, TBusinessObject>
{
TBusinessObject MethodA(TEntity entity);
Void MethodB(IEntityContext<TEntity> context, TBusinessObject obj, TEntity entity);
}

Затем, когда дело доходит до реализации вашего класса:

class MyClassThatDoesThisStuff : IMyInterfaceName<Farm, FarmBo>
{
    internal FarmBo MethodA(Farm entity);
    internal Void MethodB(IEntityContext<Farm> context, FarmBo obj, Farm entity);

}

так далее...

Вы также можете заставить ваши классы происходить от чего-то:

interface IMyInterfaceName<TEntity, TBusinessObject> 
            where TEntity : EntityBase,
                  TBusinessObject : BusinessObjectBase
{
...
}
person Neil    schedule 27.01.2011
comment
Если Уильям хочет сделать методы internal, интерфейсы — это неправильный путь, поскольку все методы интерфейса должны быть public - person Matten; 28.01.2011
comment
@Matten - неправда - все они должны быть ограничены видимостью интерфейса. - person Neil; 28.01.2011
comment
Вы говорите, что если интерфейс internal, класс реализации должен реализовать методы интерфейса с internal? - person Matten; 28.01.2011
comment
@mattten - видимость не должна быть внутренней, большей или более заметной, т.е. может быть общедоступной или внутренней. - person Neil; 28.01.2011
comment
@Neil - Хорошо, с этими знаниями ваше решение с интерфейсами, конечно, предпочтительнее. Спасибо, +1 за изучение чего-то :) - person Matten; 28.01.2011
comment
@ Matten - я бы сказал, что, не зная больше о проблеме, трудно понять, как это сделать правильно. Если это всего лишь две случайные вещи, которые может делать класс, тогда да, интерфейс — правильный путь. Но если вся жизнь этого класса вращается вокруг этой функции, я бы, вероятно, выбрал общий абстрактный базовый класс. Это зависит - как это почти всегда бывает :) спасибо за +1 - person Neil; 28.01.2011
comment
Это именно то, что я искал. Спасибо. Теперь я могу просто добавить IMyInterfaceName‹Farm, FarmBo› ко всем моим классам, и я получу ошибки компилятора, если у меня не будут правильно названы методы. Для новых разработчиков также должно быть очевидно, что им нужно добавить интерфейс, если они смотрят на существующие классы. - person William; 28.01.2011

Просто чтобы показать решение с абстрактным базовым классом, см. Ответ Нила для получения дополнительной информации...

public abstract class BaseClass<A,B> {
    internal abstract A MethodA(B entity);
    internal abstract void MethodB(EntityContext context, A obj, B entity);
}

Затем расширьте этот класс:

public class AClass : BaseClass<BusinessObject, EntityObject> {
    internal override BusinessObject MethodA(EntityObject entity) {...}
    internal override void MethodB(EntityContext ctx, BusinessObject obj, EntityObject entity) {...}
}
person Matten    schedule 27.01.2011
comment
Конечно, вы можете объявить весь интерфейс внутренним. - person Neil; 28.01.2011
comment
Но не методы. Вы можете сделать интерфейс internal, но это не означает, что классы, производные от этого интерфейса, будут иметь некоторые internal и некоторые public методы. Может быть, Уильям хочет этого :) - person Matten; 28.01.2011
comment
@matten - если вы объявите интерфейс внутренним, то методы, реализующие интерфейс, могут быть внутренними. - person Neil; 28.01.2011
comment
+1 это путь, если функция вашего класса вращается исключительно вокруг этой функции, которую вы описываете. (например, адаптер). Если это так, то у вас есть возможность делать все виды других полезных вещей в общем случае с базовым классом, что сэкономит вам время в реализациях потомков. - person Neil; 28.01.2011
comment
Спасибо .. Но, как я понял, вопрос Уильямса, он хочет только применять имена методов. Я думаю, что руководство по стилю для их проекта лучше решит эту и другие проблемы;) - person Matten; 28.01.2011
comment
Интерфейсное решение — это именно то, что я искал, но я знаю, что скоро добавлю базовый класс в другую часть моего проекта. Этот код может быть полезен в этом случае. Спасибо за образец кода. - person William; 28.01.2011

На самом деле есть три подхода:

  1. Generics: когда вы указываете объект-контейнер таким образом, что он не зависит от типов BusinessObject и EntityObject. Вы можете немного расширить это, чтобы наложить ограничения на типы объектов, которые универсальные принимают в качестве параметра с универсальными ограничениями. Большая проблема заключается в том, что на самом деле трудно использовать BusinessObject и EntityObject без приведения типов и других относительно небезопасных операций.

  2. Наследование: в этом случае вы реализуете BusinessObject как базовый класс и требуете, чтобы все объекты, используемые в качестве параметра, были производными от него, и аналогично с EntityObject. Затем вы можете поместить любые методы, которые вам действительно нужно использовать, в базовый класс (и при необходимости переопределить их в производных классах).

  3. Интерфейсы: это что-то среднее между ними. Здесь вы говорите, что BusinessObject и EntityObject являются интерфейсами (традиционно их имена начинаются с I, как в IBusinessObject и т. д.). Здесь вы указываете в интерфейсе методы, которые вам требуются для реализации любого BusinessObject или EntityObject, и, следовательно, можете вызывать их в своем контейнере. объект.

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

person Mike Jones    schedule 27.01.2011