2015-04-05 1 views
0

Язык не имеет значения, это общий объектно-ориентированный вопрос (возьмите java/C# и т. Д.). Возьмите простую концепцию. У человека есть автомобиль. Человек может управлять автомобилем. Автомобиль обычно не ездит или блуждает, не так ли? `` Но, как правило, в кодах мы видим такие методы, как myCarObject.Drive().Объектно-ориентированное программирование: как правильно спроектировать, реализовать и назвать метод, который включает взаимодействие объектов?

Теперь, когда лицо вводится, и Человек управляет автомобилем:

======================= First Way ================================= 
class Car{ 
    int odometer;void drive(){ odometer++; } 
} 
class Person{ 
    void driveCar(Car c) { c.drive(); } 
} 
======================================================================== 

================================ Alternative Way ======================= 

public Car{ 
    int odometer; // car doesn't do the driving, it's the person, so no drive() 
} 
public Person{ 
    void driveCar(Car c) { c.odometer++; } 
} 

========================= = и другими способами .... ============================

=========== ================================================== ===============

Итак, мой вопрос ясен: как лучше всего использовать методы проектирования/реализации/имени в подобных случаях?

ответ

1

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

Когда поведение не может быть помещено на объект (в том смысле, что оно имеет состояние), вы помещаете его в класс Service или Utility или какую-либо аналогичную конструкцию. Authenticate - это классический пример того, что не имеет смысла ссылаться на пользователя или на любой другой объект. Для этой цели мы создаем AuthenticationProvider (или услугу, в зависимости от того, что вы предпочитаете).

В вашем сценарии персонажа и автомобиля это один объект, вызывающий поведение на другом. person.Drive(car) будет поэтому иметь в виду.

Если лицо владеет автомобилем (и автомобиль всегда принадлежит лицу), то person.Drive() может быть единственным, что вам нужно сделать. Метод Drive() будет иметь доступ к свойствам person, одним из которых является его car.

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

Имея в виду, что в реальном приложении вы будете иметь код самонастройки спрятан в другом месте, вот пример того, как это может выглядеть в C#:

Мы начнем с определения интерфейсов для вещей, может перевозить (ITransporter), и вещи, которые можно транспортировать (ITransportable):

public interface ITransportable 
{ 
    void Transport(Transportation offset); 
} 

public interface ITransporter 
{ 
    void StartTransportation(ITransportable transportable); 
    void StopTransportation(ITransportable transportable); 
} 

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

public class Transportation 
{ 
    public double Velocity { get; set; } 
    public TimeSpan Duration { get; set; } 
} 

Затем мы продолжим создавать наши реализации для этих целей. Как вы могли догадаться, Person извлекут из ITransportable и Car происходит от ITransporter:

public class Person : ITransportable 
{ 
    public Tuple<double, double> Location { get; set; } 
    private ITransporter _transporter; 

    void ITransportable.Transport(Transportation offset) 
    { 
     // Set new location based on the offset passed in by the car 
    } 

    public void Drive<TCar>(TCar car) where TCar : ITransporter 
    { 
     car.StartTransportation(this); 
     _transporter = car; 
    } 

    public void StopDriving() 
    { 
     if (_transporter != null) 
     { 
      _transporter.StopTransportation(this); 
     } 
    } 
} 

Обратите пристальное внимание на то, что я там делал. Я представил явную реализацию интерфейса в классе Person. Это означает, что Transport может быть вызван только тогда, когда на человека действительно ссылаются как ITransportable - если вы ссылаетесь на него как Person, то видны только методы Drive и StopDriving.

Теперь автомобиль:

public class Car : ITransporter 
{ 
    public double MaxVelocity { get; set; } 
    public double Acceleration { get; set; } 
    public string FuelType { get; set; } 

    private Dictionary<ITransportable, DateTime> _transportations = new Dictionary<ITransportable, DateTime>(); 

    void ITransporter.StartTransportation(ITransportable transportable) 
    { 
     _transportations.Add(transportable, DateTime.UtcNow); 
    } 

    void ITransporter.StopTransportation(ITransportable transportable) 
    { 
     if (_transportations.ContainsKey(transportable)) 
     { 
      DateTime startTime = _transportations[transportable]; 
      TimeSpan duration = DateTime.UtcNow - startTime; 
      var offset = new Transportation 
      { 
       Duration = duration, 
       Velocity = Math.Max((Acceleration*duration.Seconds), MaxVelocity)/2 
      }; 

      transportable.Transport(offset); 
      _transportations.Remove(transportable); 
     } 
    } 
} 

Следуя указаниям, которые мы поставили ранее, Car не будет иметь никаких (видимые) методы на него, либо. Если вы явно не ссылаетесь на него как ITransporter, это именно то, что происходит внутри методов Person's Drive и StopDriving.

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

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

Пример того, как эти классы могут быть использованы клиентом:

Person person = new Person(); 
Car car = new Car(); 

// car.Transport(); will not compile unless we explicitly 
// cast it to an ITransporter first. 

// The only thing we can do to set things in motion (no pun intended) 
// is invoke person.Drive(car); 

person.Drive(car); 

// some time passes.. 

person.StopDriving(); 

// currentLocation should now be updated because the Car 
// passed a Transportation object to the Person with information 
// about how quickly it moved and for how long. 
var currentLocation = person.Location; 

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

Иногда вам просто нужно быть творческим с помощью инструментов, которые у вас есть.

2

Это немного трудно сделать упрощенные примеры, как, что делать какой-либо смысл, но здесь это attemt:

Car класса, как правило, содержат методы для того, что объект может сделать сам по себе с информацией, что это имеет, например:

public class Car { 

    private bool engineOn; 

    public int Speed { get; private set; } 

    public void Start() { engineOn = true; Speed = 0; } 
    public void Accelerate() { Speed++; } 
    public void Break() { if (Speed > 0) Speed--; } 
    public void Stop() { Speed = 0; engineOn = false; }; 

} 

Person класс был бы управлять автомобилем, контролируя то, что автомобиль сам не осознает в своей среде. Пример:

public class Person { 

    public void Drive(Car car, int speedLimit) { 
    car.Start(); 
    while (car.Speed < speedLimit) { 
     car.Accelerate(); 
    } 
    while (car.Speed > 0) { 
     car.Break(); 
    } 
    car.Stop(); 
    } 

} 

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

0

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

В первом случае автомобиль, возможно, не управляет собой, но он продвигается, поэтому вы можете использовать другой глагол. Но car.advance(), возможно, не так, как человек водит автомобили ... Даже если это было вокальные команды, декодирование команды, вероятно, приведет к последовательности более основных команд.

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

 Смежные вопросы

  • Нет связанных вопросов^_^