использование функциональности другого объекта в соответствии с правильным дизайном ОО - инкапсуляция - PullRequest
1 голос
/ 29 августа 2009

Я обсуждаю правильный OO-дизайн для использования функциональности другого объекта (методов) из класса java, в то время как оба объекта остаются максимально разделенными.

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

После того, как логика реализована, вспомогательный метод (или вспомогательный объект) в дальнейшем не нужен.

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

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

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

Ответы [ 6 ]

2 голосов
/ 29 августа 2009

Но для обеспечения инкапсуляции я не должен объявлять переменную экземпляра в моем исходном классе для ссылки на этот вспомогательный объект? Это рассуждение правильно?

Нет, объявление переменной экземпляра не имеет ничего общего с нарушением инкапсуляции.

Соответствующие соображения:

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

Основные варианты:

  • локальная неразделенная переменная в одном или больше ваших функций - это создается при необходимости, уходит как как только функция выйдет. Вероятно, выбор по умолчанию, все остальное - оптимизация или особый случай.
  • переменная общего экземпляра, созданная в конструктор - создается только один раз, но длиться до тех пор, пока сам ваш объект не получит мусор собран / уничтожен.
  • переменная общего экземпляра, созданная в первый раз используется - как указано выше, но с задержкой создание за счет сложности.
  • внешняя статическая функция - нет объекта, поэтому нет проблем с временем жизни. Подходит для чего-то без внутреннего состояния и простого интерфейса, в противном случае вы получите неявное время жизни объекта, управляемое только комментариями к функциям (как в функциях библиотеки C, таких как strcpy ).

Расширенный выбор:

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

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

1 голос
/ 29 августа 2009

Правильный ответ зависит от характера отношений между классом помощника (H) и используемым для него методом (M), и классом исходного объекта (C). Вы упомянули несколько ключевых моментов:

  • Вы не хотите помещать необходимую логику в C.
  • Вместо этого вы поместили его в H.
  • H.M() используется только один раз C.
  • H не зависит от клиента.
  • Поскольку вы говорите «очевидно, что мне понадобится ссылка на этот вспомогательный объект, чтобы использовать его метод», я предполагаю, что вы можете работать только с экземплярами H, а M() является методом экземпляра.

Есть несколько решений:

  • Оцените, будет ли M лучше как статический метод. Это очень убедительное использование статического метода, если я когда-либо видел его. Вы ничего не упоминаете о сохранении состояния H

  • Используйте шаблон Стратегии. Если H.M() представляет определенный способ выполнения чего-либо, тогда H является объектом Стратегии в шаблоне для C. Если есть другие H -подобные классы с похожими M() методами, это разные стратегии, которые вы можете выбрать.

0 голосов
/ 30 июня 2010

Я обсуждаю правильный, ОО-дизайн для использовать функциональность другого объекта (методы) из класса Java, в то время как оба объекта остаются разъединенными насколько это возможно.

Вы прикладываете слишком много усилий для этого. Разделение не означает отсутствие связи вообще. Если вы собираетесь использовать вспомогательный объект один раз, просто передайте его в качестве параметра коду блока, который его использует, или попросите ваш класс получить его экземпляр с фабрики.

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

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

0 голосов
/ 30 августа 2009

Так это сценарий? Это вопросы?

Class MyClass {
   private SomeState myState;

   public voic oneMethod() {
         // Q1 : what type is **ahelper** ?
         // Q2 : where do we declare it ?
         // Q3 : where do we initialise it?
         aHelper.doSomeWork();

         // update your state depending upon the results
   }
}

Q1. Я думаю, что вы должны объявить aHelper в качестве интерфейса

 HelperInterface aHelper;

Тогда мы не связаны с какой-либо конкретной реализацией.

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

 HelperInterface aHelper = ? what here?
 aHelper.soSomeWork();

Q3. Инициализируйте либо в конструкторе, либо с помощью ленивого получателя с использованием фабрики.

public MyClass(HelperInterface injectedHelper) {
    aHelper = injectedHelper;
}

Это может сделать тестирование очень простым, ваши тесты могут внедрить класс Mocked Helper.

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

private getHelper() {
    if (aHelper == null ){ make the helper, perhaps using a factory }

    return aHelper     
}
0 голосов
/ 29 августа 2009

Статические методы сложно тестировать, или, скорее, статические методы, вызываемые в другом методе, трудно имитировать. Мне легко делать хороший дизайн, думая с точки зрения тестирования. Если метод является нестатическим членом, вы можете легко выполнить модульное тестирование кода, вызывающего этот метод, без его тестирования. Ты со мной? Допустим, метод m использует сеть, чтобы делать вещи. Если этот метод является статическим, каждый раз, когда вы тестируете код, который использует метод m, он будет делать что-то в сети. И если происходит сбой сети, ваш тест не пройден, но не код, который вы хотите протестировать. Ты видишь? Если он не статичен, вы можете смоделировать объект с помощью метода и заставить его всегда возвращать «ОК».

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

0 голосов
/ 29 августа 2009

Почему бы вам не использовать статический метод или использовать вспомогательный объект singleton ?

...