Целостность закона Деметры сохраняется с помощью вспомогательной функции (удалено две точки)? - PullRequest
2 голосов
/ 27 декабря 2011
public House
{
    WeatherStation station;

    public float getTemp() {
        //Law of Demeter has been violated here
        return station.getThermometer().getTemperature();
    }
}

public House
{
    WeatherStation station;

    public float getTemp() {
        //Law of Demeter has been preserved?
        Thermometer thermometer = station.getThermometer();
        return getTempHelper(thermometer);
    }

    public float getTempHelper(Thermometer thermometer)
    {
        return thermometer.getTemperature();
    }
}

В приведенном выше коде вы можете видеть два разных определения класса Хауса. У обоих есть функция getTemp (), первая из которых нарушает Закон Деметры, но вторая сохраняет ее (согласно книге Head First Design Patterns).

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

Так может ли кто-нибудь объяснить, почему второй пример кода не нарушает закон? Что действительно отличает второй метод от первого?

Ответы [ 2 ]

3 голосов
/ 27 декабря 2011

Я полагаю, что по этому вопросу можно много спорить, но, как я понимаю, цель Закона Деметры была бы ...

"Вы не хотите получатьТермометр со станции. Вы хотите получить температуру со станции. "

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

Итак, с этой целью вы получите что-то вроде этого:

public House
{
    private WeatherStation _station;

    public House(WeatherStation station)
    {
        _station = station;
    }

    public float GetTemperature()
    {
        return _station.GetTemperature();
    }
}

public WeatherStation
{
    private Thermometer _thermometer;

    public WeatherStation(Thermometer thermometer)
    {
        _thermometer = thermometer;
    }

    public float GetTemperature()
    {
        return _thermometer.GetTemperature();
        // This can be replaced with another implementation, or any other
        // device which implements IThermometer, or a hard-coded test value, etc.
    }
}

Этоприводит к нескольким уровням вложения, что кажется немного неприятным.Но имейте в виду, что каждый уровень, в настоящее время называемый одной и той же вещью, означает что-то немного другое.Это не действительно дублирование кода, если дублированный код имеет другое значение.Позже вы можете разорвать цепочку примерно так:

public House
{
    private WeatherStation _station;

    public House(WeatherStation station)
    {
        _station = station;
    }

    public WeatherInfoDTO GetCurrentWeather()
    {
        var info = new WeatherInfoDTO();
        info.Temperature = _station.GetTemperature();
        //...
        return info;
    }
}

public WeatherInfoDTO
{
    //...
}

public WeatherStation
{
    private Thermometer _thermometer;

    public WeatherStation(Thermometer thermometer)
    {
        _thermometer = thermometer;
    }

    public float GetTemperature()
    {
        return _thermometer.GetTemperature();
        // This can be replaced with another implementation, or any other
        // device which implements IThermometer, or a hard-coded test value, etc.
    }

    //...
}

Не задавая жестко верхний уровень реализации Thermometer, вы обеспечите простой рефакторинг для поддержки чего-то подобного.*

1 голос
/ 27 декабря 2011

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

Другими словами, оба примера осведомлены о деталях реализации станции, поэтому удалениеметод getThermometer () станции сломает оба примера.Сказать, что второе лучше, вроде как нарушает дух закона, по моему мнению.

...