Большая декомпозиция классов в Java

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

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

любые практики, разделяя такие объекты в Java?

5 ответов


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

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

посмотреть делегация Википедии шаблон обсуждение например.

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

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

...
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 StaffInformation staffInformation;
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(); }
...

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


Я видел некоторые случаи, когда это решается наследованием: скажем, класс 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-адреса, передаваемые объекту при выполнении), и геттеры для предоставления значений, доступных для различных шаблонов страниц (я думаю, это был JSPs).

Итак, разбивка сгруппированных вещей логически, например, предполагая, что класс назывался MyAction, был MyActionBasicArgs (поля и сеттеры для основных аргументов CGI), расширенный MyActionAdvancedArgs (advanced-option args), расширенный MyActionExposedValues (геттеры), расширенные MyActionDependencies (сеттеры, используемые Spring dependency injection, не-CGI args), расширенные MyAction (который содержал фактический метод execute ()).

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


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

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

  1. нет необходимости импортировать весь пакет / BigClass, чтобы получить несколько методы.
  2. изменения кода, связанные функциональность не требует перекомпиляция / повторное развертывание целый пакет/BigClass.
  3. меньше используемой памяти для выделения/освобождения объектов, поскольку вы используете меньшие классы.

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

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

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

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

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

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

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

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

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

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


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

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

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

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