Это плохой дизайн?
У меня есть класс под названием Chicken and in 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);
}
}
}
Ответы
Ответ 1
Почему функция UpdateChicken
не является членом класса Chicken
?
Таким образом, вам не нужно будет передавать экземпляр объекта Chicken
, а просто вызвать метод Update
в существующем экземпляре:
Chicken chicken = new Chicken("Name", "Description");
chicken.Update();
Как правило, лучше всего инкапсулировать все методы, которые работают с определенным классом внутри этого класса, а не разделять их на отдельный класс "помощник". Пусть их куры управляют собой!
Ответ 2
Вся идея объектно-ориентированного программирования - думать о объектах, способных действовать на себя.
Итак, вы должны просто использовать chicken.Update()
для обновления цыпленка.
Ответ 3
Я собираюсь использовать ваш класс 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);
}
}
Ответ 4
Почему бы вам не дать вашему классу 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 actually not nice.";
// ... change all the properties as you want
Ответ 5
Объекты должны инкапсулировать функциональность. Функциональность должна быть передана, чтобы обеспечить гибкость инкапсулирующего объекта.
Итак, если вы сохраняете цыпленка, вы должны перейти в функции репозитория. Если у вас есть расчеты цыплят, и они подвержены изменениям, они также должны быть переданы.
class Chicken
{
IChickenCalculations ChickenCalculations;
IChickenRepository ChickenRepository;
Chicken(IChickenCalculations chickenCalculations, IChickenRepository chickenRepository)
{
ChickenCalculations = chickenCalculations;
ChickenRepository = chickenRepository ;
}
Calculate()
{
ChickenCalculations.Calculate(this);
}
Update()
{
ChickenRepository.Update(this);
}
}
Обратите внимание, что как в этом примере цыпленок способен выполнять вычисления сам по себе и упорствовать, не имея никакого знания о том, как выполнять вычисления или сохраняющиеся вещи (в конце концов, это только цыпленок).
Ответ 6
Пока я понимаю, что нет Chicken
, на вашем реальном объекте может быть метод Update
, правильно?
Я думаю, вы должны попробовать представить что-то еще, чем "обновление" в терминах языка. Невозможно понять, что делает обновление. Он просто обновляет "данные" в "Цыпленке"? В таком случае какие данные? А также, если вам разрешено обновлять экземпляр Курицы?
Я бы предпочел посмотреть что-то вроде
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
Ответ 7
Для многопоточной среды лучше всего подходит отдельный класс, такой как ChickenCalculations. Когда вам нужно выполнить несколько других шагов, кроме того, что делает chicken.Update(), вы можете сделать это с помощью класса ChickenCalculations. Поэтому, если несколько классов, которые создают экземпляр и вызывают методы на Chicken, не нужно беспокоиться о тех вещах, о которых заботится класс ChickenCalculations.