0

Скажем, у меня есть структура XML-файл для импорта в базу данных:Разве добавление новых полей (и/или методов) нарушает OCP (открытый закрытый принцип)?

<Flight> 
    <FlightName>FN 7777</FlightName> 
    <Passengers> 
     <American> 
      <FirstName>Michael</FirstName> 
      <LastName>Smith</LastName> 
     </American> 
     <American> 
      <FirstName>Jack</FirstName> 
      <LastName>Brown</LastName> 
     </American> 
     <German> 
      <FirstName>Hans</FirstName> 
      <LastName>Schaefer</LastName> 
     </German> 
     <Ukranian> 
      <FirstName>Sergei</FirstName> 
      <LastName>Osipenko</LastName> 
      <CanSpeakRussian>true</CanSpeakRussian> 
     </Ukranian> 
    </Passengers> 
</Flight> 

Основываясь на первоначальном требовании я создал эту классовую структуру:

public class Flight 
{ 
    public Flight() 
    { 
     Passengers = new List<IPassenger>(); 
    } 
    public string FlightNr { get; set; } 
    public List<IPassenger> Passengers { get; set; } 
    public void SomeMethod() 
    { 
     ... 
    } 

} 

public interface IPassenger 
{ 
    string FirstName { get; set; } 
    string LastName { get; set; } 
} 

public class German : IPassenger 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

public class American : IPassenger 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

public class Ukranian : IPassenger 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public bool CanSpeakRussian { get; set; } 
} 

Во втором выпуске проекта, мы имели 3 новые требования:

  1. Полет должен иметь узел назначения
  2. Все пассажиры должны га в Номер паспорта
  3. Все американские пассажиры должны иметь номер социального страхования (номер социального страхования)

XML-структура этих требований:

<Flight> 
    <FlightName>FN 7777</FlightName> 
    <Destination>Chicago</Destination> 
    <Passengers> 
     <American> 
      <FirstName>Michael</FirstName> 
      <LastName>Smith</LastName> 
      <PassportNr>US123456</PassportNr> 
      <SSN>123-45-6789</SSN> 
     </American> 
     <American> 
      <FirstName>Jack</FirstName> 
      <LastName>Brown</LastName> 
      <PassportNr>US556699</PassportNr> 
      <SSN>345-12-9876</SSN> 
     </American> 
     <German> 
      <FirstName>Hans</FirstName> 
      <LastName>Schaefer</LastName> 
      <PassportNr>DE112233</PassportNr> 
     </German> 
     <Ukranian> 
      <FirstName>Sergei</FirstName> 
      <LastName>Osipenko</LastName> 
      <CanSpeakRussian>true</CanSpeakRussian> 
      <PassportNr>UK447788</PassportNr> 
     </Ukranian> 
    </Passengers> 
</Flight> 

Вопрос-1: Если изменить код структура, как показано ниже, нарушает принцип Open Close Принцип SOLID?

public class Flight 
{ 
    public Flight() 
    { 
     Passengers = new List<IPassenger>(); 
    } 
    public string FlightNr { get; set; } 
    public string Destination { get; set; } 
    public List<IPassenger> Passengers { get; set; } 
} 

public interface IPassenger 
{ 
    string FirstName { get; set; } 
    string LastName { get; set; } 
    string PassportNr { get; set; } 
} 

public class German : IPassenger 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string PassportNr { get; set; } 
} 

public class American : IPassenger 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string PassportNr { get; set; } 
    public string SSN { get; set; } 
} 

public class Ukranian : IPassenger 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public bool CanSpeakRussian { get; set; } 
    public string PassportNr { get; set; } 
} 

Вопрос-2: Как вы думаете, что я должен использовать абстрактный класс для этой структуры для лучшего и более короткого кода? Если да, есть ли какие-либо негативные последствия для проверки?

public class Flight 
{ 
    public Flight() 
    { 
     Passengers = new List<Passenger>(); 
    } 
    public string FlightNr { get; set; } 
    public string Destination { get; set; } 
    public List<Passenger> Passengers { get; set; } 

} 

public abstract class Passenger 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string PassportNr { get; set; } 
} 

public class German : Passenger 
{ 
} 

public class American : Passenger 
{ 
    public string SSN { get; set; } 
} 

public class Ukranian : Passenger 
{ 
    public bool CanSpeakRussian { get; set; } 
} 
+2

Что вас беспокоит, следуя за SOLID на зуб или выполняйте свои требования? У вас есть новое требование. Добавление свойства не нарушает никаких правил. Даже если у вас есть потребители вашего кода там, кто ссылается на ваш код, добавление свойства в класс не повредит, если вы не измените интерфейс [как вы обнаружили]. Вы должны задать себе вопрос, кто использует ваш код? Если это всего лишь внутренний код, и у вас есть контроль над полной базой, вы можете сделать что угодно. Если вы хотите поддерживать обратную совместимость во многих случаях, вам нужно использовать абстрактные классы. –

+0

@ T.S. На самом деле, я просто пытаюсь понять принцип открытого/закрытого. Я подготовил этот пример, чтобы понять границы. Насколько я понимаю из нескольких статей, даже у Джона Скита есть некоторые проблемы с этим принципом (ссылка на эту ссылку) (https://codeblog.jonskeet.uk/2013/03/15/the-open-closed-principle- в обзоре/)). –

+0

Все шаблоны программирования, принципы и догмы имеют критику. Вы действительно не должны следовать ни одному из этих вопросов. Это полезно, хотя, если вы объясните кому-то, как что-то работает, что у вас есть подсистема, разработанная с использованием P-шаблона, bla-bla, они вас понимают. Просто посмотри.NET, чтобы лучше понять принцип o-c. .NET во многих случаях имеет интерфейс и абстрактный класс. Таким образом, вы можете написать полностью пользовательский реализованный объект или полагаться на базовую реализацию, предоставленную вам. Но тогда это зависит от вас, вы хотите оставить свой объект открытым или закрытым - закрытым классом. –

ответ

0

Цитата из Agile принципов, моделей и практик Роберт Мартин (необходимо прочитать):

модули, которые соответствуют OCP имеют два основных атрибутов.

Они открыты для расширения. Это означает, что поведение модуля может быть расширено. По мере изменения требований приложения мы можем расширить модуль с новыми поведением, которые удовлетворяют этим изменениям . Другими словами, мы можем изменить то, что делает модуль.

Они закрыты для модификации. Расширение поведения модуля не приводит к изменению исходного кода или двоичного кода модуля . Бинарная исполняемая версия модуля, будь то в библиотеке , DLL или .EXE-файле, остается нетронутой.

(Обратите внимание, что, когда он говорит, «модуль», это не обязательно означает, что сборка какого-то, но и меньший масштаб вещь, как классы и единица компиляции.)

Что важно здесь является то, что это около Поведение. У вас есть иерархия структур данных, более или менее лишенная какого-либо поведения. Так как это так, трудно сказать, нарушает ли ваш код OCP или нет, поскольку это не совсем применимо.

Нарушения OCP обычно связаны с некоторыми коммутаторами или условными обозначениями на основе типов. Любое поведение класса Flight зависит от того, какой тип пассажиров он имеет?Если да, то он может прийти в различных формах, таких как:

if (passenger is Kefiristani) { 
    performSuperStrictSecurityChecks(passenger); 
} 

или переместить этот уродливый код в performSecurityChecks способе Passenger класса, а затем в Flight классе вобще

passenger.performSecurityChecks(); // non-virtual call 

Или просто сделайте это ООП, ура полиморфизма!

passenger.performSecurityChecks(); // virtual call 

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

Первый пример нарушает SRP, из всех вещей. Он также может нарушать OCP на уровне сборки, если ваши классы не являются внутренними, а кто-то за пределами вашей сборки может расширить их, и теперь им нужно будет изменить свой код, чтобы он работал. Если они являются внутренними, то это спорный вопрос, нарушает ли это OCP или нет, но нарушение SRP еще хуже в любом случае.

Второй пример определенно нарушает OCP. Расширение класса не должно заставлять авторов производного класса изменять что-либо в базовых классах. На самом деле они, возможно, не смогут.

Последний пример не нарушает OCP. Класс все еще расширяется, а расширение не требует изменений, просто записывая код новый. Вот что такое OCP.

Чтобы ответить на ваш первоначальный вопрос: добавляет ли новые поля (и/или методы) OCP? Нет, сам по себе это не так. Но когда добавление или изменение чего-либо в производном классе заставляет вас внести некоторые изменения в базовый класс (ы), тогда OCP будет нарушен.

Одна вещь об OCP заключается в том, что практически невозможно никогда не сломать ее вообще. Определенное требование для производного класса может заставить вас что-то изменить в базовом классе. Если вы планируете заранее все возможные изменения, вы рискуете чрезмерно задуматься над всем, а потом что-то, о чем вы не думали, приходит и кусает вас. Но когда это произойдет, вы можете разобраться с ним по-разному, и лучше добавить виртуальный метод к базовому классу только один раз, а затем переопределить этот метод в новых производных классах, вместо того, чтобы снова и снова попадать в одну и ту же ловушку, пока ваш код становится фактически недостижимым.

+0

Последний абзац является лучшим. все сводится к тому, «кто хранитель кода», и он предназначен для расширения для начала. да? –

+0

Спасибо @Sergey, это достаточно ясно. –