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

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

3 голосов
/ 12 февраля 2009

Представьте, что у меня есть интерфейс под названием IVehicle.

Из этого интерфейса я извлекаю несколько конкретных типов, таких как автобус и автомобиль (все могут двигаться, замедляться, выключать двигатель и т. Д.). Мой интерфейс не имеет полей.

Будет ли плохой дизайн иметь один класс с полями (например, максимальная скорость транспортного средства) и использовать его для каждого конкретного типа? Это будет плохой дизайн? Кроме того, если я храню состояние в этом классе (например, использую поля), то должно ли оно быть статическим?

Спасибо

Ответы [ 12 ]

7 голосов
/ 12 февраля 2009

Я бы добавил TopSpeed к интерфейсу как свойство:

interface IVehicle
{
    void Move();
    void SlowDown();
    void SwitchOffEngine();

    Int32 TopSpeed { get; }
}

Затем вы реализуете интерфейс в ваших конкретных сущностях следующим образом:

class Car : IVehicle
{
    public void Move() { }
    public void SlowDown() { }
    public void SwitchOffEngine() { }

    public int TopSpeed
    {
        get { return 120; }
    }
}

class Bus : IVehicle
{
    public void Move() { }
    public void SlowDown() { }
    public void SwitchOffEngine() { }

    public int TopSpeed
    {
        get { return 98; }
    }
}

Я думаю, что это будет лучший подход и не требует базового класса.

Причина, по которой я считаю, что это лучший подход, поскольку TopSpeed - это свойство, которое является специфическим для реализации и не будет иметь никакого значения в базовом классе. Так как базовый класс потребует, чтобы любая реализация в любом случае установила это значение, я думаю, что лучше изменить интерфейс, чтобы включить его.

7 голосов
/ 12 февраля 2009

Вы говорите об абстрактном классе, и нет, это не плохой дизайн. Вот для чего они (в основном интерфейсы, но с некоторой базовой реализацией).

Состояние не должно храниться статически. Будет полная реализация этого класса (в форме одного из производных классов) каждый раз, когда вы создаете Автомобиль, Автобус и т. Д.

6 голосов
/ 12 февраля 2009

Вы не нарушаете ОО, в вашем случае -

IVehicle определяет "если вы автомобиль, что вы должны делать"

AbstractVehicle определяет "если вы автомобиль, это минимум, который вы должны делать, и это то же самое, что и вы"

ConcreteVehicle определяет «Я - транспортное средство, это мой способ делать то, что я должен делать, поскольку я - транспортное средство»

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

2 голосов
/ 12 февраля 2009

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

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

2 голосов
/ 12 февраля 2009

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

2 голосов
/ 12 февраля 2009

Просто чтобы прояснить ситуацию ...

Интерфейс не может иметь полей.

Однако он может иметь свойства. Свойства, которые неизменно должны быть реализованы через код объектами, которые реализуют интерфейс.

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

Другими словами, если у вас есть это:

IVehicle vehicle = GetVehicle();
// want to get speed of vehicle?
Car car = (Car)vehicle;
Double speed = car.Speed;

Общие свойства должны быть помещены в общий интерфейс. Поскольку транспортное средство является движущимся объектом, скорость имеет смысл иметь там.

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

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

Однако общий план использования интерфейсов вместо простой иерархии классов для отделения доступа к объекту и его использования от его фактического типа и идентичности является хорошей идеей и приводит к хорошему дизайну.

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

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

1 голос
/ 13 февраля 2009

Будет ли плохим дизайном иметь один класс с полями (например, максимальная скорость) транспортного средства) и использовать это каждым из конкретные типы? Будет ли это плохо дизайн

номер

ИМХО, трудности с наследованием в C ++ (я не очень хорошо знаю случай с Java и другими языками) помогли установить традицию, согласно которой «неконечные классы должны быть абстрактными» (на самом деле это элемент «более эффективного»). C ++ »Скотта Мейерса, если я хорошо помню). Но, во всяком случае, помещение полей и конкретной функциональности в базовый класс - это неплохой дизайн ОО.

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

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

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

Кроме того, если я сохраню состояние в этом класс (например, использовать поля), затем это должно быть статичным?

Номер * * тысяча двадцать-одна

1 голос
/ 13 февраля 2009

Если вы «добавляете поле к интерфейсу», он перестает быть интерфейсом и становится базовым классом. Это может быть плохо или нет, в зависимости от ваших предпочтений.

Шаблон использования интерфейса и исключения базовых классов с переменной-членом вполне подойдет. Это особенно полезно в ситуациях, когда вы передаете интерфейс в другую часть системы.

Но без определенного контекста не существует абсолютного правила, гласящего: «Вы должны использовать интерфейс в качестве базового класса». Использование закрытой переменной-члена в вашем базовом классе может быть хорошим первым приближением к функциональности, которую вы ищете. Другое решение, использующее закрытую функцию, определенную в производном классе, является более многословным и только по этому факту может усложнить понимание вашего кода (хотя, если все в вашей организации делают это, это не проблема).

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

Является ли переменная-член статической или нет, это отдельная проблема. Вы должны сделать член статическим, если и только он не изменяется в течение жизни класса. «Максимальная скорость» звучит так, как будто она соответствует этим критериям. Некоторые языки, такие как c ++, имеют концепцию «статических функций», результаты которых не меняются. Я полагаю, что вы могли бы также использовать это, но я нахожу эту дополнительную концепцию слишком запутанной, чтобы иметь смысл.

1 голос
/ 12 февраля 2009

Будет ли плохой дизайн иметь один класс, который имеет поля (например, максимальная скорость транспортного средства) и использовать его для каждого конкретного типа? Это будет плохой дизайн?

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

Кроме того, если я храню состояние в этом классе (например, использую поля), то должно ли оно быть статическим?

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


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

interface IVehicle
{
  void accelerate();
}

sealed class VehicleProperties
{
  const int topSpeed;
}

class VehicleImplementation : IVehicle
{
  //const data
  const VehicleProperties vehicleProperties;
  //non-const instance/state data
  int currentSpeed;
  //ctor
  VehicleImplementation(VehicleProperties vehicleProperties)
  {
    this.vehicleProperties = vehicleProperties;
  }
  //implement IVehicle methods
  void accelerate()
  {
    if (currentSpeed => vehicleProperties.topSpeed)
      return;
    ... else todo: accelerate ...
  }
}

class Car : VehicleImplementation
{
  //static properties associated with the Car type
  static VehicleProperties carProperties = new VehicleProperties(120);
  //ctor
  Car()
    : VehicleImplementation(carProperties)
  {}
  //IVehicle methods already implemented by
  //the VehicleImplementation base class
}
0 голосов
/ 15 июля 2009

Да, это плохая идея. Ваше определение IVehicle является настолько общим, но вы все еще сделали некоторые предположения. Во-первых, атрибут max-speed может не иметь одинаковых единиц измерения. У ребенка типа Автомобиль может быть максимальная скорость, измеренная в уставных милях, в то время как один из Лодки или Самолета будет использовать морские мили. Подробнее об этой проблеме обсуждается в этой статье Codewrights Tale об идентификации объекта . Есть тонкие проблемы с наследованием, которые наша культура приняла, но никогда полностью не исследовала. Если вы хотите понять эти тонкости, изучите, как ФАУ должна приблизиться к определениям того, что означает «самолет», чтобы правила могли быть определены с некоторым подобием здравомыслия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...