Я нарушаю "Закон Деметры"?

Недавно я узнал о Закон Деметры.

Как и многие вещи, я понял, что это то, что я уже делал, но не имел названия. Есть несколько мест, хотя я, кажется, их нарушаю.

Например...

У меня может быть объект Address:

public class Address : IAddress
{
   public string StreetAddress { get; set; }
   public string City { get; set; }
   public int Zip { get; set; }
}

и объект Customer:

public class Customer : ICustomer
{
   private IAddress address;

   Customer()
   {
      Address = null;
   }
   public string Name { get; set; }
   public IAddress
   {
      get
      {
         if (address == null)
         {
            address = new Address();
         }
         return address;
      }
      set
      {
         address = value;
      }
   }
}

Хорошо, это поддельный код, поэтому вам, вероятно, не придется прыгать на меня, чтобы использовать IoC, чтобы исключить new Address() или что-то еще, но это в значительной степени пример того, что я делаю. Я не включил интерфейсы, поскольку я надеюсь, что они очевидны.

Я бы использовал его в своем коде для таких вещей, как int zip = customer.Address.Zip; и customer.Address.City = "Vancouver";

Как я понимаю, я нарушаю Закон Деметры, манипулируя деталями адреса от Клиента.

И снова, похоже, что и каркас. В конце концов, не будет ли адрес .City.Length быть нарушением? Должен ли я добавлять методы Address для обработки доступа к свойствам строки? Возможно нет. Итак, зачем загромождать адрес?

Я не могу просто добавить методы для адреса, которые относятся только к клиенту. У меня есть объекты Member, Employee, Dependent, Vendor, Employer и т.д., У которых также есть адреса.

Есть ли лучший способ справиться с этим? С какими проблемами я рискую, если буду использовать адрес так, как сейчас?

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

public class Address extends AddressInterface
{
   private String m_city;

   public String getCity() { return m_city; }
   public void setCity(String city) { m_city = city; }
}

Я должен признать, что customer.getAddress().setCity("Vancouver"); вызывает больше тревог, чем customer.Address.City = "Vancouver"; сделал для меня. Возможно, мне нужно некоторое время переключиться на Java.

Ответ 1

Эта статья: http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx имеет большое объяснение обсуждаемых вами проблем.

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

Мартин Фаулер: "Я бы предпочел, чтобы это называлось иногда полезным предложением Деметры".

Ответ 2

Нарушения закона Деметры - это примеры кодового запаха с именем "Несовершенная близость". Чтобы удалить этот запах, вы можете реорганизовать свой код, спрятав внутренности адреса и методы реализации в Customer, которые делегируют адрес. Таким образом, вы уважаете инкапсуляцию по адресу внутри Клиента.

Пример:

public class Customer extends ICustomer{
    private Address address;
    ....

    public void setCity(String city){
        address.setCity(city);
    }

    public String getCity(){
        return address.getCity();
    }
}

Надеюсь, что это поможет.

Ответ 3

Проблема здесь в том, что Address является ValueObject. Вы бы никогда не меняли город, не меняя почтовый индекс.

public class Customer extends ICustomer{
    private Address address;
    ....

    public void setAddress(String street, String city, int zip){
        address = Address.new(street, city, zip);
    }
    // or even better but i'm not sure if it valid C#
    public void setAddress(int zip){
        address = Address.lookup(zip);
    }
}