Иерархическое структурирование функций внутри класса (C #) - PullRequest
1 голос
/ 13 января 2012

возможно ли структурировать функции внутри класса, чтобы некоторые функции были доступны только через заранее написанный идентификатор?

Я постараюсь сделать мой вопрос более понятным на (плохом) примере ;-) Мой классный автомобиль получил функции drive, openDoor, closeDoor, startEngine и т. Д. Но, чтобы было ясно, я бы хотел чтобы получить доступ к этим функциям следующим образом:

car.drive()
car.door.open()
car.door.close()
car.engine.start()

Я пытался использовать структуры и вложенные классы, но я не думаю, что это были правильные пути, потому что мне не нравится создавать объект для каждого «идентификатора», который я использую.

Есть ли "чистые" способы сделать это?

Спасибо!

Изменить: Я не уверен, имеет ли это значение, но вот некоторая дополнительная информация:

  • "Автомобиль" -Класс - это Сингелтон
  • кроме функций, ни у двигателя, ни у дверей, ни у какой-либо другой части моего автомобиля нет никаких других свойств (ура. Действительно плохой пример!)

Ответы [ 4 ]

3 голосов
/ 13 января 2012

Вложенные классы будут правильным подходом.

Учтите, что все объекты Door будут иметь методы Open и Close, а объект Car будет иметь несколько экземпляров объекта Door (2, может быть 4 или даже больше).

Аналогично, каждый объект Car будет иметь экземпляр объекта Engine, который может иметь значение Start ed, Stop ped, и замену масла (ChangeOil).

Тогда каждый из этих классов будет расширяемым за пределы класса Car. Если вы хотите изменить часть кода внутри вашего Door класса, все ваших Car объектов, имеющих Door s, автоматически наследуют эти изменения. Если вы хотите заменить двигатель автомобиля более мощным, вы можете легко это сделать, передав новый экземпляр объекта Engine объекту Car.

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

И на тот случай, если мне не удастся убедить вас, что вложенные классы являются правильным подходом, я в заключение отмечу, что они также являются единственным способом достижения того, чего вы хотите. То есть, помимо хакерских решений, таких как добавление псевдопространства имен в начало каждого имени функции (т. Е. Car.DoorOpen), что ужасно и на самом деле ничего вам не дает.

1 голос
/ 17 февраля 2012

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

public class Car
{
    public static Car Instance { get; private set; }
    static Car() { Car.Instance = new Car(); }
    private Car() { }
    public static class Door
    {
        public static void Close()
        {
            /* do something with Car.Instance */
        } 
    }
}

Использование статических классов дает следующий синтаксис:

Car.Door.Close();

C # позволяет вкладывать определения классов, которые не имеют формального понятия «внутренние типы» или «внутренние классы», как это делают другие языки.

Это отличный пример плохого моделирования / проектирования компонентов.

Рассмотрим следующее, которое является более приемлемым и более изящным решением:

public interface ICar
{
    IDoor Door { get; set; } 
}

public class Car : ICar
{
    public IDoor Door { get; set; }
}

public interface IDoor
{
    void Open();
    void Close();
}

public class Door : IDoor
{
    public override void Open() { /* do something */ }
    public override void Close() { /* do something */ }
}

С учетом вышесказанного можно использовать синтаксис инициализатора C #:

var car = new Car
{
    Door = new Door()
};

car.Door.Open();

Если ваш захватс постоянно нуждающимся в наборе "new XYZ" вы также можете запечь инициализацию в конструкторе.Правильно выполненный с шаблоном DI «бедняк», он должен выглядеть следующим образом:

public class Car : ICar
{
    public IDoor Door { get; set; }
    public Car()
        : this(new Door())
    {
    }
    public Car(IDoor door)
    {
        this.Door = door;
    }
}

Это избавляет от необходимости выполнять инициализацию как часть создания и позволяет вводить новые / разные типы дверей в стек.

var common = new Car();
var lambo = new Car(new LamboDoor());

В любом случае вызывающий код выглядит одинаково:

common.Door.Open();
lambo.Door.Open();

Наконец, вы можете рассмотреть структуру Composition или DI, а не вставлять операторы new () в код своей реализации.,В любом случае, наиболее подходящий подход заключается в использовании свойств и построении законной объектной модели, которая выражает ваше намерение.Вложенные типы не дают искомого синтаксиса, если не используются статические члены, и такой подход очень редко бывает хорошей идеей, я видел его только для упрощенных одноэлементных реализаций, конечно же, никогда для определения API / Framework / Model.

1 голос
/ 13 января 2012

Нет - вам нужно иметь дверь с вложенными классами со своими собственными методами.Вы можете добавить свой дескриптор к имени метода.Таким образом, у вас будет

car.DoorOpen();
car.DoorClose();

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

car.Door.Open();
0 голосов
/ 13 января 2012

Просто мои 2 чувства.

Мне нравится @Code Grey идея вложенных классов. Но чтобы добиться правильного порядка вызова методов, я думаю, вам нужно вложить машину под Driver . Водитель будет делать что-то вроде этого.

public class Driver
{
   public void Drive(Car car)
   {
      car.drive();
      car.door.open();
      car.door.close();
      car.engine.start();
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...