У меня есть следующий вариант использования, много кода, который был тесно связан с конкретным типом (скажем, Concrete1). Позже выяснилось, что конкретный тип нужно изменить, поэтому определил интерфейс. Например
Class ABC {
virtual int foo() = 0;
virtual int getType() = 0;
}
class Concrete1 : public ABC {
int foo() {
... }
int getType() {
return 1;
}
}
class Concrete2 : public ABC {
int foo() {
... }
int getType() {
return 2;
}
}
Для создания объектов использовался статический фабричный паттерн. Таким образом, все места, где был создан объект new Concrete1, заменяются на ABCFactory :: createType ().
Теперь в коде есть много мест, где мне нужно проверить, является ли объект, возвращаемый createType, Concrete1 или Concrete2, и, соответственно, выполнить соответствующую логику (так много if else в коде :().
Я хочу избежать большого количества if else в коде как части этого изменения. Какие-либо предложения?
Меня больше всего беспокоит
if (abc.getType() == 1) {
...
} else if (abc.getType() ==2) {
...
}





Можете ли вы переместить в класс места, где вы обнаруживаете типы объектов вне класса? Таким образом, функциональность (которая, по-видимому, зависит от конкретного класса) действительно связана с соответствующим классом?
весь смысл использования интерфейсов заключается в том, чтобы вы могли использовать полиморфизм, что означает, что вам никогда не нужно проверять, к какому типу относится экземпляр. это очень неприятный запах кода (см. Refacotring Фаулера). переместите условную логику в конкретные классы и добавьте функцию, которая будет обрабатывать ее в интерфейсе
РЕДАКТИРОВАТЬ (добавление примера кода, так как первоначальная публикация была сделана с мобильного телефона):
Вы пытаетесь сделать:
void Main(string[] args)
{
Bird bird = BirdFactory.GetPigeon();
if (bird.GetType().Equals(typeof(Duck)))
{
Console.WriteLine("quack");
}
else if (bird.GetType().Equals(typeof(Pigeon)))
{
Console.WriteLine("coo coo");
}
}
Вместо этого попробуйте:
interface Bird
{
void Speak();
}
class Duck : Bird
{
void Speak()
{
Console.Write("quack");
}
}
class Pigeon : Bird
{
void Speak()
{
Console.Write("coo coo");
}
}
void Main(string[] args)
{
Bird bird = BirdFactory.GetPigeon();
bird.Speak();
}
Если вы проверяете и / или включаете тип во время выполнения, вы можете рассмотреть возможность использования информации о типе во время выполнения, если она доступна для вашего компилятора. Это добавляет некоторые накладные расходы, но выполняет то, что вы пытаетесь сделать, без создания или поддержки пользовательской методологии.
В отличие от пуристов, я на самом деле не сторонник того, чтобы «добавить функциональность в класс, чтобы вы могли использовать полиморфизм, чтобы обойти переключение типов». Однако оба подхода являются допустимыми (хотя метод «в классе» не всегда возможен и / или концептуально чист).
Причина, по которой переключение типов не одобряется, заключается в том, что это быстро увеличивает сложность вашего кода и быстро снижает ремонтопригодность. Когда вы начинаете переключать типы, вы в конечном итоге помещаете переключатели по всему коду, и если что-то изменится, теперь у вас есть много мест, где можно изменить
В соответствии с другими ответами мне кажется, что по крайней мере часть кода в блоках if / else необходимо переместить внутри конкретных классов в качестве новой виртуальной функции. Это позволит вам использовать полиморфизм, а не включать типы с домашним шаблоном отражения.
Поместите ... в реализацию еще одного виртуального метода:
if (abc.getType() == 1) {
... // A
} else if (abc.getType() == 2) {
... // B
}
Поместите A и B так:
class ABC {
virtual int foo() = 0;
virtual void doIt() = 0; // choose a proper name
};
class Concrete1 : public ABC {
int foo() {
... }
void doIt() {
... // A
}
};
class Concrete2 : public ABC {
int foo() {
... }
void doIt() {
... // B
}
};
И измените свое если на
abc.doIt();
Как сказал другой, в этом и смысл динамической отправки! Помимо того, что он более лаконичен, он также никогда не «забудет» обработать тип. Выполняя свой переход, вы могли молча не обрабатывать конкретный тип, потому что вы пропустили обновление кода в этом месте, когда вы представили новую реализацию. Также помните, что в ABC есть виртуальный деструктор.
+1 за хорошие имена переменных. Это намного легче понять, чем просто еда и бары.