Это плохой дизайн? - PullRequest
       35

Это плохой дизайн?

21 голосов
/ 08 апреля 2011

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

Chicken chicken = new Chicken("Name","Description")


public void UpdateChicken(Chicken chicken)
{ 
   chicken.Update(chicken);
}

Это хорошо илиэто создает проблемы, если так, то лучше иметь другой класс, такой как ChickenCalculations и сделать что-то вроде:

public void UpdateChick(Chicken chicken)
{
    ChickenCalculations.Update(chicken);
}

Вот реализация:

Chicken chicken = new Chicken("Bob","Coolest Chicken", 4, 123, 5, 388, true, false, true);

Chicken anotherChicken = new Chicken()
anotherChicken.Update(chicken);
chicken.Update(chicken)

Вотболее практичный пример вместо использования курицы:

public class AirlineBooking
{
    int BookingId {get;set;}
    string Name {get;set;}
    string Description {get;set;}
    decimal Price {get;set;}
    decimal Tax {get;set;}
    string seat {get;set;}
    bool IsActive {get;set;}
    bool IsCanceld {get;set;}


    public AirlineBooking(string name, string description, decimal price, 
                          decimal tax, string seat, bool isActive, bool isCanceled)
    {
        Name = name;
        Description = description;
        Price = price;
        Tax = tax;
        Seat = seat;
        IsActive = isActive;
        IsCanceled = isCanceled;
    }

    public Update(AirlineBooking airlineBooking, int id)
    {
          //Call stored proc here to update booking by id
    }

    public class BookingSystem
    {
       //Create new booking
       AirlineBooking booking = new AirlineBooking("ticket-1",
                                                   "desc",150.2,22.0,
                                                   "22A",true, false);

       //Change properties and update.
       booking.Name ="ticket-2";
       booking.Description = "desc2";
       booking.Price = 200.52;
       booking.Tax = 38.50;

       public void UpdateBooking(AirlineBooking booking, int id)
       {
            /* This is the meat of the question, should the passed in booking to
               update itself or should I have a Service Class , such as
               AirlineBookingOperations with an update method. */
            booking.Update(booking,id);
       }
    }
}

Ответы [ 7 ]

27 голосов
/ 08 апреля 2011

Почему функция UpdateChicken не является членом класса Chicken?

Таким образом, вам не придется передавать экземпляр Chicken object, но лучше просто вызвать метод Update в существующем экземпляре:

Chicken chicken = new Chicken("Name", "Description");
chicken.Update();

Как правило, лучше всего инкапсулировать все методы, которые работают с конкретным классом внутри этого классавместо того, чтобы разбивать их на отдельный «вспомогательный» класс.Пусть куры сами справятся!

9 голосов
/ 08 апреля 2011

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

Так что вам просто нужно использовать chicken.Update(), чтобы обновить курицу.

8 голосов
/ 12 апреля 2011

Я собираюсь использовать ваш класс AirlineBooking в качестве примера, потому что многие люди, похоже, запутались из-за примера Chicken.

Некоторое введение:

Принцип единой ответственности гласит, что объект должен иметь одиночную ответственность и что он должен заниматься только теми вещами, которые соответствуют этой ответственности. Например, TaxCalculator должен только отвечать за расчет налога, а не, например, за конвертацию валюты - это работа CurrencyConverter.

Часто это действительно хорошая идея , так как это означает, что ваше приложение структурировано на куски кода, каждое из которых несет единую ответственность, облегчая понимание и безопасное изменение. Другой способ выразить это в том, что у класса или модуля должна быть одна-единственная причина для изменения, например «Изменился способ расчета налога» или «Изменился способ преобразования валюты».


Вопросы, которые вам нужно задать себе:

  • За что отвечает AirlineBooking?
  • Является ли обновление бронирования авиабилетов частью этой ответственности?

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

Альтернативно, другой способ думать об этом заключается в том, что если я добавлю Update метод к AirlineBooking, это будет означать, что:

  • Если система бронирования переключается на использование веб-службы, а не хранимой процедуры, то класс AirlineBooking необходимо изменить.
  • Если изменяется инкапсуляция бронирования авиакомпании (возможно, можно приостановить бронирование или теперь записано название авиакомпании), то AirlineBooking необходимо изменить.

т.е. AirlineBooking теперь имеет много разных причин для изменения, и поэтому он не должен отвечать за "Обновление"


Короче, я бы, наверное, сделал это:

public class AirlineBooking
{
    public int BookingId {get;set;}
    /* Other properties */
}

public class BookingSystem
{
    public void UpdateBooking(AirlineBooking booking, int id)
    {
        // Call your SP here.
    }
}

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

Например, если AirlineBooking «осведомлен» (т. Е. Имеет ссылку на) систему бронирования, то вы можете добавить «вспомогательный» метод, например:

public class AirlineBooking
{
    public void Update(int id)
    {
        this.bookingSystem.UpdateBooking(this, id);
    }
}
3 голосов
/ 08 апреля 2011

Объекты должны инкапсулировать функциональность.Функциональность должна быть передана для обеспечения гибкости инкапсулирующего объекта.

Итак, если вы сохраняете курицу, вы должны передать функциональность репозитория.Если у вас есть расчеты цыплят, и они подвержены изменениям, они также должны быть переданы.

class Chicken
{
   IChickenCalculations ChickenCalculations;
   IChickenRepository ChickenRepository;
   Chicken(IChickenCalculations chickenCalculations, IChickenRepository chickenRepository)
   {
       ChickenCalculations = chickenCalculations;
       ChickenRepository = chickenRepository ;
   }

   Calculate()
   {
       ChickenCalculations.Calculate(this);
   }
   Update()
   {
       ChickenRepository.Update(this);
   }
}

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

3 голосов
/ 08 апреля 2011

Почему бы вам не дать вашему классу Chicken метод "Обновить (некоторые параметры ...)"?Затем вы можете просто создать курицу на

Chicken chicken = new Chicken("Name", "descr");

и обновить на:

chicken.Update(myparameters..);

РЕДАКТИРОВАТЬ

public class Chicken
{
  public Chicken(string name, string description)
  {
     this.Name = name;
     this.Description = description;
  }

  public string Name { get; set; }
  public string Description { get; set; }

  // Fill in all the other properties!

  public int EggsDroppedInLife { get; set; }
}

И теперь выВы можете использовать свой куриный класс следующим образом:

Chicken chicken = new Chicken("Harry", "Nice chick");
chicken.NumberOfEggs = 123;
chicken.Description = "Oh no, it's actually not nice.";
// ... change all the properties as you want
1 голос
/ 08 апреля 2011

Хотя я понимаю, что Chicken не может быть Update метод на вашем реальном объекте, верно?

Я думаю, вы должны попытаться ввести что-то еще, кроме «обновления» в терминах языка. Нет способа действительно понять, что делает обновление. Это просто обновляет "данные" в Цыпленке? В таком случае, какие данные? А также, вам следует разрешить обновлять экземпляр Chicken как этот?

Я бы предпочел увидеть такие вещи, как

chicken.CanFly = false;
if(chicken.CanFly)  // inherited from Bird :)
    ckicken.FlyTo(point);
else
    chicken.WalkTo(point);

Вот довольно интересное упражнение в ООП: http://milano -xpug.pbworks.com / f / 10080616-extreme-oop.pdf

0 голосов
/ 08 апреля 2011

Для многопоточной среды лучше использовать отдельный класс, такой как ChickenCalculations.Когда вам нужно выполнить несколько других шагов, кроме того, что делает chicken.Update (), вы можете сделать это с помощью класса ChickenCalculations.Так что если нескольким классам, которые создают экземпляры и вызывают методы в Chicken, не нужно беспокоиться о тех же вещах, о которых заботится класс ChickenCalculations.

...