2009-01-02 4 views
1

У меня есть следующий прецедент, много кода, который был плотно соединен по конкретному типу (скажем, 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 (abc.getType() == 1) { 
    ... 
} else if (abc.getType() ==2) { 
    ... 
} 

ответ

9

Весь смысл использования интерфейсов заключается в том, что вы можете использовать полиморфизм, что означает, что вам никогда не придется проверять, какой тип экземпляра. это очень большой запах кода (см. Fowlers Refacotring). перейти условную логику конкретных классов и добавить тэ функцию, которая будет обрабатывать его интерфейс

EDIT (Добавление пример кода, поскольку начальное сообщение было сделано с мобильного телефона):

Вы пытаетесь сделать:

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(); 
} 
+0

+1 для хороших имен переменных. Это гораздо легче понять, чем просто фосов и баров. –

0

вы можете переместить места, которые вы обнаруживают типы объектов вне класса в класс? Таким образом, функциональность (которая, по-видимому, зависит от конкретного класса) фактически связана с соответствующим классом?

0

Если вы проверки и/или включения типа во время выполнения, вы можете рассмотреть возможность использования Run Time Type INFORMATI on, если он доступен для вашего компилятора. Он добавляет некоторые накладные расходы, но он выполняет то, что вы пытаетесь сделать, не создавая или не поддерживая собственную методологию.

В отличие от пуристов, я на самом деле не «помещаю функциональность в класс, чтобы вы могли использовать полиморфизм, чтобы обойти тип переключения». Оба являются действительными подходами (хотя метод «в классе» не всегда возможен и/или концептуально чист).

+0

Причина того, что включение типов неодобрительно, заключается в том, что это быстро увеличивает сложность вашего кода и быстро снижает ремонтопригодность. Когда вы начинаете включать типы, вы в конечном итоге ставите переключатели по всему вашему коду, и если что-то изменится, теперь у вас есть много мест для изменения – IAmCodeMonkey

1

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

2

Поместите ... в реализации еще одного виртуального метода:

if (abc.getType() == 1) { 
    ... // A 
} else if (abc.getType() == 2) { 
    ... // B 
} 

Put 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(); 

As другой сказал, это точно точка динамической отправки! Помимо того, что он более краток, он никогда не «забывает» обрабатывать тип. Выполняя свой переключатель, вы можете молча обращаться с определенным типом, потому что вы пропустили обновление кода в этом месте, когда вы ввели новую реализацию. Также помните о наличии виртуального деструктора в ABC.

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