Разложение большого класса в Java

Я только начал изучать Java и любопытно, что это хорошая практика в Java для хорошей декомпозиции объектов? Позвольте мне описать проблему. В большом программном проекте это всегда большие классы, такие как "core" или "ui" , которые имеют множество методов и предназначены как посредники между меньшими классами. Например, если пользователь нажимает кнопку в каком-либо окне, этот класс окна отправляет сообщение в класс "ui" . Этот класс "ui" ловит это сообщение и действует соответствующим образом, делая что-то с пользовательским интерфейсом приложения (посредством метода вызова одного из его объектов-членов) или путем отправки сообщения в "ядро" приложения, если это что-то вроде "exit application" или "start network" соединение.

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

Любые методы, деля такие объекты на Java?

Ответ 1

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

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

См. Пример публикации Wikipedia.

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

В том случае, если вы начали с одного большого класса, содержащего следующие методы, каждый из которых содержит бизнес-логику, среди многих других методов:

...
public boolean isManagement() { ... }
public boolean isExecutive() { ... }
public int getYearsOfService() { ... }
public Date getHireDate() { ... }
public int getDepartment() { ... }
public BigDecimal getBasePay() { ... }
public BigDecimal getStockShares() { ... }
public boolean hasStockSharePlan() { ... }
...

тогда этот большой объект мог бы в своем конструкторе создать вновь созданный объект StaffType и вновь созданный объект PayInformation и вновь созданный объект StaffInformation, и изначально эти методы в большом объекте выглядели бы так:

// Newly added variables, initialized in the constructor (or as appropriate)
private final StaffType      staffType;
private final PayInformation payInformation;
private final PayInformation payInformation;

...

public boolean isManagement() { return staffType.isManagement(); }
public boolean isExecutive() { return staffType.isExecutive(); }
public int getYearsOfService() { return staffInformation.getYearsOfService(); }
public Date getHireDate() { return staffInformation.getHireDate(); }
public int getDepartment() { return staffInformation.getDepartment(); }
public BigDecimal getBasePay() { return payInformation.getBasePay(); }
public BigDecimal getStockShares() { return payInformation.getStockShares(); }
public boolean hasStockSharePlan() { return payInformation.hasStockSharePlan(); }
...

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

Ответ 2

Я видел некоторые случаи, когда это решается наследованием: пусть класс Big заботится о 5 разных вещах, и (по разным причинам) все они должны быть в одном классе. Итак, вы выбираете произвольный порядок наследования и определяете:

BigPart1 // all methods dealing with topic #1
BigPart2 extends BigPart1 // all methods dealing with topic #2
...
Big extends BigPart4 // all methods dealing with the last topic.

Если вы действительно можете сложить вещи, чтобы разлом имел смысл (Part2 фактически использует материал из Part1, но не наоборот и т.д.), тогда, возможно, это имеет смысл.

Место, где я это видел, находится в WebWorks, где у одного класса было множество методов getter/setter - сеттеры, используемые для инъекции зависимостей (например, URL-адреса, переданные объекту при выполнении), и геттеры для делая ценности доступными для различных шаблонов страниц (я думаю, что это были JSP).

Итак, разбивка сгруппирована по логике, например, если класс был вызван MyAction, там были MyActionBasicArgs (поля и сеттеры для основных аргументов CGI), расширенные MyActionAdvancedArgs (расширенные аргументы), расширенные MyActionExposedValues ​​(getters) расширенные MyActionDependencies (сеттеры, используемые <инъекцией зависимостей Spring, не-CGI-аргументы), расширенные MyAction (в которых содержится фактический метод execute()).

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

Ответ 3

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

Преимущества этого - снижение и производительность зависимостей.

  • Не нужно импортировать все пакет /BigClass, чтобы получить несколько методы.
  • Изменения кода в связанных функциональности не требуется перекомпилировать/перераспределить весь пакет /BigClass.
  • Меньше памяти для выделения/деаллокации объектов, поскольку вы используете более мелкие классы.

Ответ 4

Я не знаю, почему у вас будет такой большой класс.

Я полагаю, что если вы используете генератор кода gui builder и ленитесь об этом, вы можете оказаться в такой ситуации, но codegen обычно заканчивается неприятно, если вы не возьмете себе контроль.

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

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

Старайтесь не изменять код сгенерированного кода, если его можно избежать. Включение бизнес-логики в "рамку" с гранатом - это просто ужасный шаблон дизайна. Большинство генераторов кода не очень помогают в этом, поэтому попробуйте сделать одно минимальное редактирование, чтобы получить то, что вам нужно от внешних классов (подумайте, MVC, где genned-код - ваш View, а код, который вы редактируете, должен быть в вашей модели и контроллер).

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

Если вы не говорите о сгенерированном коде - хорошо в вашем классе "Бог", делает ли он всего одну небольшую работу и делает это хорошо? Если нет, вытащите "Job", поместите его в свой класс и делегируйте ему.

Является ли ваш класс GOD полностью учтен? Когда я видел такие огромные классы, я обычно видел много строк для копирования/вставки/редактирования. Если есть достаточное сходство с копией и прошлым и отредактируйте какой-либо раздел, то этого достаточно, чтобы разделить эти строки на один кусок кода.

Если ваш большой класс - это GUI-класс, рассмотрите декораторы - многоразовые и перемещайте материал из своего основного класса. Двойная победа.

Я думаю, ответ на ваш вопрос заключается в том, что в Java мы просто используем хорошее OO, чтобы гарантировать, что проблема не возникает в первую очередь (или мы не... Java, безусловно, невосприимчив к проблемам, которые вы говорите о любом другом языке)

Ответ 5

Да, С# предоставляет частичные классы. Я предполагаю, что это то, о чем вы говорите, когда говорите:

С# решает такую ​​проблему, позволяя разбить реализацию класса на несколько источников файлы: вы можете разделить объект God любым способом, который вы выберете, и он будет работать.

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

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