2008-12-11 1 views
8

Я только что закончил смотреть видео с чистого кода Google на YouTube (см. link, первая статья) об удалении if утверждений из вашего кода и вместо этого используйте полиморфизм.Как бы вы реорганизовали это условное использование полиморфизма?

После просмотра видео я просмотрел какой-то код, который я писал перед просмотром видео, и заметил некоторые места, где я мог бы использовать этот метод, главным образом, места, где такая же логика была реализована много раз. Пример:

У меня есть код вроде этого.

public int Number 
{ 
    get 
    { 
     string returnValue; 
     if (this.internalTableNumber == null) 
      returnValue = this.RunTableInfoCommand(internalTableName, 
                TableInfoEnum.TAB_INFO_NUM); 
     else 
      returnValue = this.RunTableInfoCommand(internalTableNumber.Value, 
                TableInfoEnum.TAB_INFO_NUM); 
     return Convert.ToInt32(returnValue); 
    } 
} 

Что RunTableInfoCommand это не очень важно, но самое главное, что у меня есть много свойств, с точно такой же if statments единственное, что меняется это TableInfoEnum.

Мне было интересно, может ли кто-нибудь помочь мне реорганизовать это так, чтобы он все еще делал то же самое, но без каких-либо заявлений if?

+0

Почему голос? –

+1

Потому что вы изначально сказали «использовать полиморфизм», затем сказали: «О нет, мне не нужен полиморфизм» - и я уже потратил немало того, что, как я думал, потратил впустую, отвечая на вопрос, который вам неинтересен.Теперь, когда «использовать полиморфизм» вернулся, я удалил проголосовавший голос и восстановил свой ответ – tvanfosson

+1

Простите, я не хотел тратить время на то, что в один из этих дней мне все еще интересно узнать, как люди это сделают. Снова очень жаль. –

ответ

8

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

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

+0

Да, я делаю попытку заменить все ваши if-утверждения некоторым полиморфным подходом, немного сумасшедшим. – BobbyShaftoe

+0

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

0

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

public class Thing 
{ 
    public IValueFetcher ValueFetcher { get; set; } 

    public int Number 
    { 
    get 
    { 
     return this.ValueFetcher.GetValue<int>(/* parameters to identify the value to fetch */); 
    } 
    } 
} 

Это будет заботиться о много повторяющегося кода и уменьшить зависимость от источника значения к интерфейсу.

Я думаю, что в какой-то момент вы, вероятно, будете иметь оператор if, поскольку вам все равно нужно решить, какую версию RunTableInfoCommand вы хотите вызвать.

4

На самом деле вы реализуете нечто вроде шаблона стратегии. Начать с определения суперкласса можно называть его AbstractTableInfoCommand. Этот класс может быть абстрактным, но должен указывать метод под названием runTableInfoCommand().

Затем вы можете определить несколько подклассов, каждый из которых реализует метод runTableInfoCommand(). Затем ваш класс, имеющий свойство Number, будет иметь новое свойство типа AbstractTableInfoCommand (может вызвать его tableInfoCommand), которое будет создано в одном из конкретных подклассов AbstractTableInfoCommand.

код будет тогда:

public int Number 
    { 
     get 
     { 

      return this.tableInfoCommand.runTableInfoCommand(); 
     } 
    } 

Таким образом, вы можете создать NullTableInfoCommand и SomeOtherTableInfoCommand и т.д. Преимущество заключается в том, что если у вас есть новое условие для возвращения tableinfocommand затем добавить новый класс, а не редактировать этот код.

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

0

Я предполагаю, что internTableName и InternalTableNumber - это своего рода ценность для того же самого.Почему бы не обернуть его внутри класса и передать экземпляр этого класса this.RunTableInfoCommand как так:

public int Number 
     { 
      get 
      { 
       string returnValue; 
       internalTableClass myinstance(parameters); 
       return Convert.ToInt32(this.RunTableInfoCommand(myinstance, TableInfoEnum.TAB_INFO_NUM)); 
      } 
     } 

, если вы все еще хотите использовать полиморфизм, то вы можете сделать это в этом классе из-за перегрузки, например, giveInternalTableIdentifier что возвращает номер или имя

код здесь может выглядеть следующим образом:

public int Number 
     { 
      get 
      { 
       string returnValue; 
       internalTableClass myinstance(parameters); 
       return Convert.ToInt32(this.RunTableInfoCommand(myinstance.giveInternalTableIdentifier, TableInfoEnum.TAB_INFO_NUM)); 
      } 
     } 

и код internalTableClass будет тривиальным (с использованием: internalAbstractTableClass, и два класса, которые наследуют от него, один давая имя, а другой номер)

0

EDIT 2 Как я действительно решаю проблему.

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

private int? internalTableNumber; 
private int InternalTableNumber 
{ 
    get 
    { 
     if (!internalTableNumber.HasValue) 
     { 
      internalTableNumber = GetValueFromTableName(internalTableName); 
     } 
     return internalTableNumber; 
    } 
    set 
    { 
     internalTableNumber = value; 
    } 
} 

public int Number 
{ 
    get 
    { 
     string value = this.RunTableInfoCommand(InternalTableNumber, 
               TableInfoEnum.TAB_INFO_NUM); 
     return Convert.ToInt32(value); 
    } 
} 

EDIT Использование полиморфизма ...

Давайте предположим, что ваш текущий класс с именем Foo, то я бы реорганизовать его на два класса, FooWithName и FooWithNumber. FooWithName будет классом, который вы использовали бы, когда у вас есть имя таблицы, и FooWithNumber будет классом, который будет использоваться, когда у вас есть номер таблицы. Затем я буду писать каждый класс с помощью метода Number - на самом деле, я напишу интерфейс IFoo, а также каждый из них будет реализован так, чтобы их можно было использовать взаимозаменяемо.

public interface IFoo 
{ 
    int Number { get; }| 

} 

public class FooWithName : IFoo 
{ 
    private string tableName; 
    public FooWithName(string name) 
    { 
     this.tableName = name; 
    } 

    public int Number 
    { 
     get { return this.RunTableInfoCommand(this.tableName, 
             TableInfoEnum.TAB_INFO_NUM); 
    } 

    ... rest of class, including RunTableInfoCommand(string,int); 
} 

public class FooWithNumber : IFoo 
{ 
    private int tableNumber; 
    public FooWithNumber(int number) 
    { 
     this.tableNumber = number; 
    } 

    public int Number 
    { 
     get { return this.RunTableInfoCommand(this.tableNumber, 
             TableInfoEnum.TAB_INFO_NUM); 
    } 

    ... rest of class, including RunTableInfoCommand(int,int); 
} 

вы бы использовать его в качестве так:

IFoo foo; 

if (tableNumber.HasValue) 
{ 
    foo = new FooWithNumber(tableNumber.Value); 
} 
else 
{ 
    foo = new FooWithName(tableName); 
} 

int number = foo.Number; 

Очевидно, что если у вас есть много КРП-Then-Else конструкций в существующем классе, это решение не реально улучшить его много. Это решение создает IFoo, используя полиморфизм, а затем просто использует методы интерфейса, не заботясь о реализации. Это можно было бы легко расширить, чтобы наследовать общую реализацию RunTableCommand (int) в абстрактном классе, который наследует IFoo и является базовым классом для FooWithNum и FooWithName.

0

Я бы реорганизовать его таким образом:

table = this.internalTableNumber == null ? internalTableName : internalTableNumber.Value; 
return Convert.ToInt32(this.RunTableInfoCommand(table, TableInfoEnum.TAB_INFO_NUM)); 

Любовь тройной оператор.

1

Я чувствую, что ваш код в порядке. Удобочитаемый. Просто. (И я надеюсь, что это сработает). Если этот блок кода повторяется n раз, вам нужно удалить дублирование, применяя метод Extract.

Рефакторинг, который вы указываете, предназначен для замены повторяющихся корпусов коммутаторов .. не просто, если такие утверждения, как в вашем примере. Replace Conditional With Polymorphism. Помните «самое простое, что работает». Это означает минимальное количество классов и методов, необходимых для выполнения работы.

+0

Если каждое свойство выполнялось так же, если/then/else, оно все равно было бы хорошим кандидатом на полиморфизм. – tvanfosson

+0

Почему бы не изолировать конструкцию if в методе, который может вызвать любое свойство? Строка рефакторинга RCWP: «У вас есть условие, которое выбирает различное поведение в зависимости от типа объекта». - ИМХО .. он здесь не применяется. – Gishu

+0

То, что я сделал сейчас, я действительно не думал, когда я бросил это здесь. –

0

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

С RunTableInfoCommand(), internalTableNumber и internalTableName все, кажется, быть членами одного и того же же класса, хороший, легкий рефакторинг может быть, чтобы добавить перегрузку RunTableInfoCommand(), который просто принимает значение TableInfoEnum и делает логику выяснить, какие другие RunTableInfoCommand() перегрузки должна быть вызвана:

private string RunTableInfoCommand(TableInfoEnum infoEnum) 
{ 
    if (this.internalTableNumber == null) { 
     return this.RunTableInfoCommand(internalTableName, infoEnum); 
    } 

    return this.RunTableInfoCommand(internalTableNumber.Value, infoEnum); 
} 

Затем многочисленные сайты вызовов, которые имеют ту же if логику решения могут быть свернуты в:

returnValue = this.RunTableInfoCommand(TableInfoEnum.TAB_INFO_NUM);  
             // or whatever enum is appropriate 
0

Как бы я реорганизовал эти конкретные утверждения if, чтобы использовать полиморфизм?

Ну ... я бы не стал. Полиморфизм не нужен, чтобы исключить утверждения if.

Обратите внимание, что если заявления, как не «плохие» сами по себе, то если заявления вызваны выбора представления, где

(internalTableNumber == null) ==> 
    internalTableName else internalTableNumber.Value 

Эта ассоциация предполагает недостающее класс, то есть было бы больше смысла имеют класс InternalTable, в котором есть innerTableName и internalTablenumber, так как эти два значения сильно связаны правилом null-check.

  • Этот новый класс может обеспечить свойство TableNumber, выполнивший internalTableNumber == NULL проверки
  • и RunTableInfoCommand может занять InternalTable экземпляр в качестве параметра (но не надо, смотрите следующий пункт).
  • Но еще лучше, новый класс должен иметь фасад для метода RunTableInfoCommand (который предположительно статический), который выполнял целочисленное преобразование.

Предполагая InternalTable экземпляр называется подтекстом, то переработан код концерна будет выглядеть:

public int Number 
{ 
    get 
    { 
     return iTable.RunTableInfoCommand(TableInfoEnum.TAB_INFO_NUM); 
    } 
} 

Это инкапсулировать нуль-чек в InternalTable классе. Изменение оригинальной подписи RunTableInfoCommand является необязательным.

Обратите внимание, что это не «заменить операторы if полиморфизмом», но он исключает утверждения if из класса потребления через инкапсуляцию.

+0

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

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

  • Нет связанных вопросов^_^