Как работает композиция в C#?
У меня есть класс автомобиля:
public class Car
{
private String _Brand;
private String _Model;
private Engine _Engine;
public Car(){
}
public String Brand
{
get { return _Brand; }
set { _Brand = value; }
}
public String Model
{
get { return _Model; }
set { _Model = value; }
}
public Engine Engine
{
get { return _Engine; }
set { _Engine = value; }
}
}
И класс двигателя:
public class Engine
{
private int _Code;
private DateTime _Year;
public Engine(){
}
public int Code
{
get { return _Code; }
set { _Code = value; }
}
public DateTime Year
{
get { return _Year; }
set { _Year = value; }
}
}
Исходя из кода от Registration.aspx.cs, я хочу создать, используя кнопку, Автомобиль и Двигатель с соответствующими отношениями (Композиция).
Тем не менее, у меня возникла путаница в отношении того, как должна вести себя композиция для этих объектов. IE, я не могу понять это, если я должен объявить: Engine = new Engine();
в конструкторе автомобиля или в случае регистрации события (кнопка, код сзади) объявить: Car car = new Car()
а потом car.Engine = new Engine();
,
Я не понимаю, как работают композиционные отношения в подобном сценарии. Существование того, где декларироватьnew
"ставит меня в смешение понятий.
С уважением
4 ответа
Вы можете немного изменить свои классы, как показано ниже, чтобы избежать стандартного кода. Мы просто используем функцию, называемую автоматически реализовать свойства.
public class Engine
{
public int Code { get; set; }
public DateTime Year { get; set; }
}
public class Car
{
public string Brand { get; set; }
public string Model { get; set; }
public Engine Engine {get; set; }
}
Пожалуйста, обратите внимание, сколько строк кодов было удалено.
Что касается вашего вопроса, вы можете создать объект типа Car
используя инициализатор объекта, как показано ниже:
var car = new Car
{
Brand = "the brand name";
Model = "the model name";
Engine = new Engine
{
Code = "the code engine";
Year = new DateTime(2017,1,1); // the year the engine manufactured.
}
}
Я бы посоветовал вам прочитать об обеих этих функциях, автоматически реализованных свойствах и инициализаторах объектов. Ниже есть две ссылки на эти функции.
Ну, это зависит от того, как вы хотите контролировать доступность / использование свойства Engine.
Случай 1: если вы хотите, чтобы Car имел полный контроль над свойством Engine, и никто другой не мог получить к нему доступ, сделайте его частным полем и создайте его экземпляр в конструкторе Car.
public class Car
{
private readonly Engine _engine;
public Car()
{
_engine = new Engine();
}
}
Случай 2: Двигатель должен быть доступен снаружи, чтобы любой мог установить его или получить к нему доступ. Это можно сделать двумя способами:
1) Внедрение в конструктор - внедрение зависимости через конструктор, обычно абстрагирующееся с использованием интерфейса, чтобы можно было легко выполнять макетирование / модульное тестирование.
public class Car
{
public Car(IEngine engine)
{
Engine = engine;
}
public IEngine Engine { get; set; }
}
var car = new Car(new Engine());
2) Установка свойства Engine вручную
public class Car
{
public Engine Engine { get; set; }
}
var car = new Car();
car.Engine = new Engine();
Все зависит от того, как вы разрабатываете свои классы. Автомобиль и двигатель очень общие. Например, если вы хотите создать Mercedes, то имеет смысл создать новый тип MercedesCar, который наследуется от Car. В этом случае вы можете создать экземпляр MercedesEngine из конструктора MercedesCar следующим образом:
public class Car
{
public Engine Engine
{
get;
private set;
}
public Car(Engine engine)
{
this.Engine = engine;
}
}
public class MercedesCar : Car
{
public MercedesCar() : base(new MercedesEngine())
{
}
}
public class Engine
{
}
public class MercedesEngine : Engine
{
}
Но если бы вы придерживались только класса Car, то нет ничего плохого в создании двигателя снаружи!
Я уже несколько лет обсуждаю этот уровень классовой архитектуры, поэтому постараюсь не показаться глупым:)
Начнем с того, что я не думаю, что мы говорим здесь о композиции: (в реальной жизни) жизненный цикл Engine
не связан / контролируется Car
, Если я правильно понимаю, оба класса пытаются представить карту реальной жизни.
Насколько я знаю, на самом деле, вы можете приобрести Engine
к Car
, но Car
не может функционировать (но может существовать) без Engine
(может быть, эти новые фантазии Теслас может?).
Этот крик не является Композицией, поскольку не описывает отношения собственности.
В зависимости от того, насколько строгим вы хотите быть, я бы назвал это Агрегацией (больше) или Ассоциацией (меньше).
Я лично больше склоняюсь к Агрегации, потому что это более сильные отношения, чем, скажем, Car
и Spoiler
,
Итак, для этого мое предложение будет:
C#
public class Engine
{
public Engine() { ... }
public int Code { get; set; }
public DateTime Year { get; set; }
}
public class Car
{
public Car(Engine engine) : this() {
this.Engine = engine;
}
public Car() {
}
public string Brand { get; set; }
public String Model { get; set; }
public Engine Engine { get; set; }
}
Codebehind
var engine = new Engine();
var car = new Car(engine);
Тот Car
определение класса говорит, что вы можете создать Car
с или без Engine
, но для меня это подчеркивает тот факт, что Engine
действительно важно для Car
поэтому конструктор принимает Engine
доступен.
О композиции
Состав Car
быть "целым" и Engine
быть "частью" означает, что Engine
Жизненный цикл полностью контролируется Car
, В этом случае вы в основном заявляете, что "После создания нового объекта Car этот объект внутренне создаст новый Двигатель, который будет утилизирован, когда объект Car больше не используется".
Код для этого будет выглядеть так:
//still the same
public class Engine
{
public Engine() { ... }
public int Code { get; set; }
public DateTime Year { get; set; }
}
public class Car
{
public Car() {
//_engine = new Engine();
}
//Engine initialisation can be either here or inside the constructor (check constructor's commented code)
private readonly Engine _engine = new Engine();
public string Brand { get; set; }
public String Model { get; set; }
//you shouldn't be able to assign an _engine from outside
public Engine Engine { get { return _engine; } }
}
Композиция Код-позади
var car = new Car();
car.Engine.Code = 123;