Как мне реализовать подтипы класса Message в Java? - PullRequest
3 голосов
/ 23 июля 2011

Я уверен, что это основной вопрос ООП - я разрабатываю систему передачи сообщений, в которой есть несколько совершенно разных форматов сообщений, но я хочу, чтобы все они могли быть помещены в PriorityBlockingQueue. Моей первой мыслью было определить abstract class Message, а затем определить подклассы, расширяющие Message для каждого из типов сообщений. Но это означает, что на принимающей стороне обработчик сообщений должен идентифицировать подкласс, чтобы знать, как обрабатывать содержимое сообщения. И единственный способ, которым я знаю, это делать с .instanceof() или Class., и это как-то не так.

Как пишет Скотт Мейерс,

Каждый раз, когда вы обнаруживаете, что пишете код вида «если объект типа T1, затем сделать что-то, но если это типа T2, то сделать что-то еще, "ударить себя.

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

Ради обсуждения, вот мои типы сообщений:

  • ConsoleMessage, идентифицирующий ConsoleObject и ObjectState.
  • CardReaderRequestMessage, не содержит ничего, а просто запрашивает «следующую карту»
  • CardReaderMessage, содержащее байтное [80] изображение карты и индикатор последней карты
  • CardPunchMessage, содержащее байт [80] изображения карты
  • CardPunchResponseMessage, не содержащий ничего, но означающий, что изображение карты было скопировано в перфорированный буфер

Я считаю, что должен знать, с каким сообщением я имею дело, поэтому я подозреваю, что не должен использовать полиморфные Сообщения. Как мне правильно спроектировать это?

===== РЕДАКТИРОВАТЬ, чтобы поднять дополнительный вопрос =====

Я пытался найти способ использовать полиморфное Сообщение без необходимости в какой-то момент идентифицировать его подкласс. Предложенный подход состоял в том, чтобы переопределить метод process() в каждом подклассе. Вот мое (упрощенное) абстрактное Сообщение и два подкласса:

public abstract class Message {

    public abstract void process() {
        // subclasses of Message implement this
    }

    public static class ConsoleMessage extends Message {
        private int obj;
        private int state;
        public ConsoleMessage(int x, int y) {
            obj = x;
            state = y;
        }
        @Override
        public void process() {
            // do something with obj and state?
        }

    public static class CardReaderMessage extends Message {
        private byte[] card;
        private boolean lastCardIndicator;
        public CardReaderMessage(byte[] c, boolean lc) {
            card = c;
            lastCardIndicator = lc;
        }
        @Override
        public void process() {
            // do something with card and lastCardIndicator
        }
}

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

waitForResumeMessage() {
    while (true) { // the following will block until a msg arrives
        Message msg = inboundMessageQueue.receiveMessage();
        msg.process();    

Но что теперь? Некоторая реализация process () перенесла некоторые данные куда-то, но в конечном итоге мне нужно написать:

        if // msg was ConsoleMessage "resume" command
            return;  // .. from waitForResumeMessage()
    } // else iterate until another message
}

Что в основном означает выяснение, к какому классу относится msg.

Я все неправильно подхожу? Я понимаю, что «ожидание» не совсем уместно в «управляемой событиями» модели, но это длительный фоновый работник. Возможно, идея использования process () более полезна для изменения состояния автомата, который управляет потоком, управляемым событиями?

Ответы [ 4 ]

2 голосов
/ 23 июля 2011

Ты не сумасшедший. Этот аргумент:

Каждый раз, когда вы обнаруживаете, что пишете код вида «если объект имеет тип T1, то что-то делаете, но если он имеет тип T2, тогда делайте что-то еще», шлепните себя.

- это только одна сторона истории. Другая сторона истории заключается в том, что так же важно (если не больше) поддерживать разделение интересов. Типичным примером является то, что вы не должны добавлять код представления в вашу модель только потому, что именно там он может быть обработан полиморфно. К сожалению, язык Java не сильно помогает в реализации обоих идеалов одновременно.

Есть много способов сохранить «параллельный, но разъединенный» код. Шаблон Visitor равен единице, как большой переключатель или блок if-instance-of. Другой вариант - Map от конкретного класса к конкретному классу.

1 голос
/ 23 июля 2011

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

Можно создать свою собственную аннотацию, которая содержит информацию, где объект сообщения может быть обработан.

Во время реализации: Вы аннотируете свои классы соответственно.

Во время выполнения: Когда вы извлекаете следующий элемент из очереди, вы можете проверить аннотацию и вызвать реализацию, способную обработать текущий экземпляр.

1 голос
/ 23 июля 2011

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

1 голос
/ 23 июля 2011

Ваша идея хороша, вам действительно следует начать с абстрактного класса или интерфейса с именем Message, и этот интерфейс должен выглядеть примерно так:

     public interface Message {
     void process();
     //some other methods
}

public class MessageType1  implements Message {
    @Override
    public void process() {
      //My special way to process this message
}
}

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

...