java: как определить возможности экземпляра подкласса, который может быть производным от суперкласса

#java #interface #capability

#java #интерфейс #возможности

Вопрос:

Чтобы сделать мой вопрос более конкретным, позвольте мне представить его как проблему:

описание ситуации:

У нас есть 3 абстрактных понятия:

 Boss:  which has a number of employees
Worker:  that can execute some types of tasks
Task:  contains the semantics needed for a worker to execute it
  

В реализации / подтипах у нас есть несколько разных типов работников и разные типы задач.
Определенный тип работника может выполнять определенные типы задач (подмножество всех типов)

проблема, которую нужно решить

Теперь у босса есть задача известного типа, которую он хотел бы видеть выполненной, он не знает, какие типы работников у него есть (только абстрактный тип / интерфейс). Какой интерфейс (ы) мог бы реализовать работник / что мог бы сделать босс, чтобы узнать?

способы решения, которые я могу придумать

Я нашел это двумя способами, но, вероятно, есть другие и лучшие способы:

1) мы создаем класс для каждого типа задачи, реализующий пустой интерфейс задачи, предоставляющий рабочему функцию execute(задача): в реализации execute(задача) попробуйте выполнить проверку типа / приведение задачи ко всем типам задач, которые может выполнять тип worker. Если ни одна из проверок типов не выполняется, мы создаем исключение taskNotSupportedException. теперь босс может давать задания работникам до тех пор, пока не будет выдано исключение.

2) теперь у нас нет классов задач, но мы определяем интерфейс возможностей для каждого типа задачи с помощью функции dotaskType(taskinfo) рабочая реализация теперь может реализовывать интерфейсы возможностей в зависимости от того, каковы ее возможности. теперь босс может проверять, пока не будет найден работник с соответствующими возможностями (проверка типов), а затем дать ему задание, зная, что он может его выполнить.

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

Я также предпочитаю 2 вместо 1, потому что 1 кажется неправильным (каскад приведений), а интерфейсы — это естественный способ определить, что может делать экземпляр класса, также с помощью интерфейсов вы могли бы группировать возможности / создавать иерархии. (также в моей текущей реализации (1) интерфейс задачи пуст, поэтому задачи не имеют много общего, и информация передается через конструктор (который был бы параметрами функций в методе 2) и извлекается с помощью gets.

Мне было интересно, каким другим способом вы бы / могли реализовать (настраивая 1 или 2?) это или какой из двух вы предпочитаете и почему. (эффективность не важна, важна модуляция / абстракция / ремонтопригодность!)

Комментарии:

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

Ответ №1:

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

Я бы добавил метод

 boolean isAbleToExecute(Task task)
  

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

Комментарии:

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

2. Да, добавление этого метода также пришло на ум. И в методе 1 это хорошая идея, но мне не очень нравится метод 1, он кажется неправильным (каскад приведений, …). Что касается этого исключения, я думаю, оно должно быть там, просто потому, что это случай, который нужно как-то обработать, и я не вижу способа для работника справиться с этой проблемой, кроме как выдать исключение (иначе говоря, сообщить своему боссу). Какова ваша точка зрения на метод 2?

Ответ №2:

Рабочий интерфейс мог бы включать метод supportedTasks, возвращающий список задач. Тогда вы могли бы поддерживать Map, сопоставляя задачу со списком работников, поддерживающих эту задачу. Тогда вашему боссу не нужно выполнять итерации, он может просто найти всех работников, которые поддерживают данную задачу.

Комментарии:

1. Среднее количество рабочих элементов будет небольшим, поэтому при их повторении не будет больших накладных расходов (то, что я имел в виду под эффективностью, не важно). И все же с точки зрения кодирования мне больше нравится isAbleToExecute (возможно, потому, что имя начинается с глагола 😉 Как насчет метода 2? Или каким-либо другим способом это можно было бы сделать (не должно быть явно превосходящим, любые идеи хороши)

2. Если я правильно понимаю метод 2, вам нужно что-то вроде if (worker instanceof taskCapability) ((taskCapability) worker). doTaskType(задача); Эта проверка типа и приведение не кажутся очень чистыми. Работники должны предоставлять средства, позволяющие узнать, какие задачи они могут выполнять. Либо isAbleToExecute, либо поддерживаемые задачи работают нормально, но если бы я был боссом, я бы хотел, чтобы мои работники индексировались так, чтобы это имело смысл для меня. И если supportedTasks возвращает набор задач, isAbleToExecute легко реализуется с помощью supportedTasks.содержит(task)

3. Вы правильно поняли, но в методе 1 тот же тип проверки и приведения выполняется рабочим, что странно, потому что он получил четкое задание от босса только потому, что оно было передано через execute (задача). Также странно определять, ЧТО (!= КАК) класс может делать в своей реализации, а не в своем интерфейсе. Вам не кажется, что метод 1 усложняет обслуживание кода, поскольку программисту, желающему добавить задачу, придется делать это двумя отдельными методами (что, если он забудет 1 ???), в то время как в методе 2 реализация интерфейса вынуждает программиста определять функцию.