Есть ли образец для этого?Общий базовый класс со специальными действиями для определенных дочерних классов - PullRequest
1 голос
/ 14 сентября 2010

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

public void doSomething(BaseThing genericThing) 
{
  if (genericThing instanceof SpecificThing)
  {
    SpecificThingProcessor stp = new SpecificThingProcessor((SpecificThing) genericThing);
  }
  else if (genericThing instanceof DifferentThing)
  {
    DifferentThingProcessor dtp = new DifferentThingProcessor((DifferentThing) genericThing);
  }
  else if (genericThing instanceof AnotherThing){
    AnotherThingProcessor atp = new AnotherThingProcessor((AnotherThing) genericThing);
  }
  else
  {
    throw new IllegalArgumentException("Can't handle thing!");
  }
}

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

Ответы [ 7 ]

11 голосов
/ 14 сентября 2010

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

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

РЕДАКТИРОВАТЬ

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

public interface IProcessor
{
    void Process();
}

public class SpecificThingProcessor : IProcessor
{
    public void Process() { /* Implementation */ }    
}

public class DifferentThingProcessor : IProcessor
{
    public void Process() { /* Implementation */ }
}

public class AnotherThingProcessor : IProcessor
{
    public void Process() { /* Implementation */ }
}

Каждый BaseThing должен реализовывать метод для возврата определенного процессора:

public abstract class BaseThing
{
    public abstract IProcessor GetProcessor();
}

public class SpecificThing : BaseThing
{
    public override IProcessor GetProcessor()
    {
        return new SpecificThingProcessor();
    }
}

public class DifferentThing : BaseThing
{
    public override IProcessor GetProcessor()
    {
        return new DifferentThingProcessor();
    }
}

И тогда ваш метод будет просто:

public void doSomething(BaseThing genericThing)
{
    IProcessor processor = genericThing.GetProcessor();
    processor.Process();
}
10 голосов
/ 14 сентября 2010

Вы должны определить метод в BaseThing, который будет переопределен конкретными вещами.

Другими словами, вы должны использовать виртуальную функцию .


Выполняемые операции не выполняются с универсальной вещью.В зависимости от конкретного типа, класс «Producer» должен быть создан для того, чтобы иметь дело с правильным типом вещи.Неуместно вызывать Producer из подклассов BaseThing

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

Для другой альтернативы это достигает моего лимита Java, но я уверен, что вы должны иметь возможность что-то делать вместеэти строки :

  • хранят список / словарь типа, конструктор процессора.
  • Получите тип экземпляра genericThing, который вы получаете
  • Найдите тип в списке и вызовите соответствующий конструктор.
3 голосов
/ 14 сентября 2010

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

abstract class BaseThing {
    abstract public void doSomething();
}

class ThingA extends BaseThing {
    public void doSomething() {
        System.out.println("ThingA...");
    }
}

class ThingB extends BaseThing {
    public void doSomething() {
        System.out.println("ThingB...");
    }
}

class ThingC extends BaseThing {
    public void doSomething() {
        throw new UnsupportedOperationException("Cannot call this on ThingC");
    }
}

, а затем

class ThingHandler {
    public void doSomething(BaseThing thing) {
         try {
             thing.doSomething();
         } catch (UnsupportedOperationException e) {
             throw new IllegalArgumentException("Can't handle thing!");
         }
    }
}

Таким образом,

ThingHandler handler = new ThingHandler();

handler.doSomething(new ThingA());  // -> ThingA...
handler.doSomething(new ThingB());  // -> ThingB...
handler.doSomething(new ThingC());  // -> IllegalArgumentException: Can't handle thing!

Вы упомянули «нужно разобраться, что это за особая вещь»так что теперь все, что вам нужно, это иметь в своем BaseThing абстрактный метод, который будет возвращать Comparator, а каждый ThingA и т. д. будет реализовывать его и возвращать соответствующий компаратор для ThingHandlerкласс для сортировки.Каждая реализация BaseThing может выполнять определенные операции или возвращать какое-либо значение, которое вам понадобится в ThingHandler (вы можете даже передать экземпляр ThingHandler в методе BaseThing.doSomething ...)

Но если Шаблон посетителя действительно то, что вам нужно, вот пример для вашей потребности:

interface IThing {
   public void accept(ThingHandler handler);
}
interface IThingHandler {
   public void visit(ThingA a);
   public void visit(ThingB b);
   //...
}

class ThingA implements IThing {
   public void accept(IThingHandler h) {
      h.visit(this);
   }
   public String getSomeValueA() {
      return "Thing A";
   }
}

class ThingB implements IThing {
   public void accept(IThingHandler h) {
      h.visit(this);
   }
   public String getSomeValueB() {
      return "Thing B";
   }
}

// ...

class ThingHandler implements IThingHandler {
   public void visit(ThingA thing) {
       // sort according to ThingA
       System.out.println(thing.getSomeValueA() + " has visited");
       doSomething(thing);
   }
   public void visit(ThingB thing) {
       // sort according to ThingB
       System.out.println(thing.getSomeValueB() + " has visited");
       doSomething(thing);
   }

   private void doSomething(IThing thing) {
       // do whatever needs to be done here
   }
}

Тогда

IThingHandler handler = new ThingHandler();
new ThingA().accept(handler);  // -> Thing A has visited
new ThingB().accept(handler);  // -> Thing B has visited
//...

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

Два

  • «кто отвечает за выполнение операции?»
  • «кто отвечает за хранение необходимых данных для выполнения операции?»

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

1 голос
/ 15 сентября 2010

Вопрос почти во всем учебнике является примером стратегии. Вы извлекаете конкретное поведение в отдельные классы, которые реализуют один и тот же интерфейс (с помощью метода типа doIt () чего-либо). И затем вы даете каждому конкретному классу ссылку на «поведенческий» объект, который вы хотите, чтобы он имел. Бонус: 1) Вы можете изменить поведение объекта во время выполнения, просто назначив ему другой «объект» поведения. 2) Вам не нужно переопределять метод (опасность переопределения методов может быть бум класса).

1 голос
/ 15 сентября 2010

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

public class BaseThing
{
  abstract public void doSomething();
}
public class SpecificThing extends BaseThing
{
  public void doSomething()
  {
    System.out.println("I'm a SpecificThing!");
  }
}
public class DifferentThing extends BaseThing
{
  public void doSomething()
  {
    System.out.println("I'm a DifferentThing!");
  }
}
public class AnotherThing extends BaseThing
{
  public void doSomething()
  {
    System.out.println("I'm an AnotherThing!");
  }
}

Если вам действительно нужно передать «вещь» в качестве параметра по какой-то причине, хорошо.Сделайте вышеописанное, затем напишите:

void doSomething(BaseThing genericThing)
{
  genericThing.doSomething();
}

Если некоторые из ваших подклассов не могут выполнять функцию и вместо этого должны выдавать сообщение об ошибке, то вместо того, чтобы сделать это абстракцией в супертипе, заставьте супертип делать«неправильная» обработка, например:

public void BaseThing
{
  public void doSomething()
    throws IllegalArgumentException
  {
    throw new IllegalArgumentException("Can't handle this thing");
  }
}
0 голосов
/ 22 сентября 2010

У вас есть несколько вариантов:
* Абстрагируйте свою функциональность в интерфейс и позвольте другим классам реализовать этот интерфейс.
* Вы можете использовать шаблон Цепочка ответственности (consisting of a source of command objects and a series of processing objects)
. * Вы также можете использовать шаблон проектирования стратегии ( algorithms can be selected at runtime)

0 голосов
/ 15 сентября 2010

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

void foo(BaseTing ting) { System.out.println("Default " + ting.name); }

void foo(TingA ting) { System.out.println("AA " + ting.name); }

void foo(TingB ting) { System.out.println("BB " + ting.name); }

Java разрешит метод, который наиболее точно соответствует типу параметра, поэтому, если у вас есть TingC, который расширяет TingB, то foo (TingB) будет вызываться до тех пор, пока foo (TingC) не будет определен в классе Processor.

Если вы собираетесь добавить намного больше действий для каждого типа вещей, например, baz (Ting), bar (Ting), bat (Ting) и т. Д., То вы можете разделить классы процессора по подтипу Ting и использовать фабричный метод для создания определенного процессора в виде шаблона стратегии.
то есть BaseProcessor, TingAProcessor, TingBProcessor.
BaseProcessor был бы хорошим кандидатом для размещения фабричного метода и должен обеспечивать реализации по умолчанию для каждого из методов, даже если реализация по умолчанию является абстрактной или просто выдает исключение. Классы специализированных процессоров должны расширяться от BaseProcessor и наследовать и переопределять операции по умолчанию.

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