Как преобразовать условные операторы в OCP (открытый и закрытый принцип) в SOLID?

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

Как лучше всего это сделать?

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

Пример, если есть переданные строки, такие как «готовить», «выпекать», «спать» и «драться». Я не хочу писать: если(действие == 'готовить'), то СДЕЛАТЬ это, или если(действие == 'выпечка'), то СДЕЛАТЬ это, или если(действие == 'спать'), то СДЕЛАТЬ это, или если(действие == 'драться') тогда СДЕЛАЙТЕ это

Скорее то, что я сделал, это создал интерфейс

public interface Iaction{
  public bool Act();
}

//then create various classes that  implemented the interface
public class Sleep: Iaction{

public bool Act(){
.....
  }
}

public class Cook: Iaction{

public bool Act(){
.....
  }
}

public class Bake: Iaction{

public bool Act(){
.....
  }
}

public class Fight: Iaction{

public bool Act(){
.....
  }
}

Основная проблема возникает, когда если действие передано, это приготовление. Я хочу выполнить только метод повара. Действие.Действие(); выполняет все классы, реализующие интерфейс.

public IHttpActionResult ThingToDo(string action){

if(string.IsnullOrEmpty(action) == false){
bool response = Iaction.Act();
  }
....
....
...
}

Есть ли способ добиться этого с помощью OCP в Solid Principle?


person Dipo    schedule 10.10.2019    source источник
comment
Пожалуйста, поделитесь кодом, где вы пытаетесь выполнить Iaction.Act().   -  person Fildor    schedule 10.10.2019
comment
Вы должны внедрить желаемое поведение (готовить, выпекать...) в классы с внедрением зависимостей.   -  person Sxntk    schedule 10.10.2019
comment
@Fildor Я отредактировал вопрос   -  person Dipo    schedule 10.10.2019
comment
Это не имеет смысла. Act не статический метод, не так ли? Должно быть что-то вроде Iaction localAction = .... ; localAction.Act();   -  person Fildor    schedule 10.10.2019
comment
@Fildor, вы не можете создать экземпляр интерфейса, если это не список.   -  person Dipo    schedule 10.10.2019
comment
Какая? Вы не можете использовать его как статический класс. Это интерфейс. Этот код не будет компилироваться.   -  person Fildor    schedule 10.10.2019
comment
@Fildor, ты прав... тогда я могу создать экземпляр на основе переданного значения действия. Полагаю, я проглядел это. Я могу создать фабричный метод, который справится с этим. Спасибо   -  person Dipo    schedule 10.10.2019


Ответы (3)


Я предлагаю вам решить эту ситуацию, заменив условную логику шаблоном стратегии.

Найдите «Замените условную логику стратегией» в книге Мартина Фаулера и прочитайте "Рефакторинг шаблонов: упрощение", Джошуа Кериевски

OCP гарантируется тем, что вы добавляете стратегии и не меняете код (в лучшем случае меняете фабрику, определяющую применяемую стратегию)

uml: умл

person Embri    schedule 10.10.2019

Создайте себе фабричный метод, который возвращает конкретный класс интерфейса на основе вашего параметра. то есть что-то вроде:


public static IAction ResolveAction(string actionName)
{
   ///logic here
}

Тогда ваш код реализации будет выглядеть примерно так:

IAction actionToExecute = ActionFactory.ResolveAction(actionName);
actionToExecute.Act();

Чтобы избежать операторов switch или if в этой функции, выполните одно из следующих действий:

  1. Используйте отражение для загрузки соответствующего класса, реализующего IAction, по запросу. Вам нужно будет правильно назвать классы, чтобы сделать это по соглашению.. т.е. «приготовить» карты для CookAction и т. д.

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

person BlackSpy    schedule 10.10.2019

Цель:

  1. Act() на основе строки: действие.
  2. Сделайте № 1, не нарушая OCP.
public interface Iaction{
  public bool Act(string action);
}

//then create various classes that  implemented the interface
public class Sleep: Iaction{
  public bool Act(string action){
    if(action == 'zzz') {
       //do something 
    } else {
       //do nothing
       ...

Если вы не можете изменить метод Act(), вы можете создать метод setAction(string action) и сохранить строку:action в унаследованных классах и Act() на основе строки.

Клиент должен быть примерно таким, как показано ниже.

var listIaction = new ArrayList<Iaction>();

public IHttpActionResult ThingToDo(string action){
   foreach(Iaction obj in listIaction){
      bool response = obj.Act(action);
   }

...

public IHttpActionResult ThingToDo2(string action){
   foreach(Iaction obj in listIaction){
      obj.setAction(action);
      bool response = obj.Act();
   }
...


person Ando    schedule 01.01.2020