Хорошо ли иметь класс, производный от базового класса и содержащий его экземпляр?


Я разрабатываю приложение, основанное на производителе/потребителе, и я застрял на представлении задачи, произведенной производителем в представлении класса.

Фактическая проблема выглядит так: производитель может произвести StandaloneTask, который может быть непосредственно потребляемым потребителем, или он может произвести CompressTask, который должен пройти через TaskDecompressor, которые извлекают его сначала в ряд StandaloneTask, которые могут быть потреблены потребителями.

Поскольку существует много общего между StandaloneTask и CompressTask, поэтому я создал базовый класс под названием TaskBase, который содержит всю эту общую информацию.

class abstract TaskBase 
{

}

class StandloneTaskType1: TaskBase
{

}

class StandloneTaskType2: TaskBase
{

}

.
.
.

class StandloneTaskTypeN: TaskBase
{

}

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

Я создал класс CompressTask следующим образом.

class CompressTask: TaskBase
{
    TaskBase task;

    //runtime parameters
}

Теперь мне кажется очень странным, что CompressTask является производным от TaskBase, а также содержит экземпляр TaskBase. Правильно ли иметь такой класс? Или есть ли лучшее классовое представление данного случая.

4 2

4 ответа:

В целом эта структура не является редкостью, вы несколько начинаете идти по путикомпозитного паттерна . Ваш CompressTask отчасти действует как композит, а ваш StandaloneTask - Как лист.

Я бы рекомендовал прочитать об этом шаблоне проектирования и потенциально подумать о том, чтобы облегчить потребителю использование любого подкласса TaskBase независимо от того, является ли он CompressTask или StandaloneTask. Это укрепит ваш дизайн и упростит потребление.

Представьте...

class Student: Person
{
    Person father;
    Person mother;
    Date dateOfEnrollment;
}

В этом есть смысл, не так ли? В принципе, в вашем классе Compressstask нет ничего плохого.

Одно важное правило проектирования ОО: предпочтение композиции над наследованием реализации. Просто потому, что StandaloneTask и CompressTask имеют много общих черт, это не делает их хорошим выбором, чтобы позволить им использовать один и тот же базовый класс. Если два класса разделяют некоторые интерфейсы, рекомендуется разложить интерфейсы на множители с помощьюнаследования интерфейса . Если два класса совместно используют некоторые реализации, вам лучше разложить реализацию на некоторый класс и внедрить его в указанный класс. два класса (т. е. композиция ).

Возвращаясь к вашему случаю, CompressTask является производным от TaskBase и также содержит экземпляр TaskBase. Это означает, что вы используете наследование реализации и композицию одновременно, что не очень хорошо пахнет. Следующий скелет как раз для вашей справки:
interface Task
{
    // some common Task interface here...
}

class TaskImpl
// Or: class TaskImpl : Task // depends on your needs
{    
    // some common Task-related implementation here...
}

class CompressTask: Task // interface inheritance, NOT implementation inheritance
{
    TaskImpl taskImpl; // contains *reusable* task-related implementation
    Task task;  // contains the target task(s) to be compressed
    // other code...
}

class StandloneTaskType1: Task
{
    TaskImpl taskImpl;
    // other code...
}

.
.
.

class StandloneTaskTypeN: Task
{
    TaskImpl taskImpl;
    // other code...
}

Еще лучше... Воображать...

class Foo // implicitly extends Object
{
    String name; // also a direct subclass of Object (at least in Java anyway)
    Integer age; // extends Number, which extends Object (at least in Javaland)
    Object theRootOfAllEvil; // a raw instance of the superclass
    int i; // the only member that is not an Object
}

Пожалуйста, простите мой Яванский, но там, откуда я родом, нет ничего плохого во всем этом;)

Все зависит от мелочей ваших занятий.