Абстрактный дизайн базового класса в Go vs C ++ - PullRequest
0 голосов
/ 02 января 2019

Я все еще изучаю способ ведения дел в Go, исходя из фона C ++. Я ищу обратную связь, сравнивающую наследование ООП с составом интерфейса.

У меня есть ситуация проектирования в программе Go, где, если бы я реализовывал на C ++, я бы решил с помощью абстрактного базового класса.

Предположим, мне нужен базовый класс, который имеет много разработчиков. Базовый класс имеет общие методы, которые работают с абстрактными элементами данных. Разные реализации Worker предоставляют операции CRUD для разных типов элементов, но все рабочие используют общие методы базового класса для общей работы.

В C ++ я мог бы сделать это так

class IItem
{
    // virtual methods
};

class IWorker
{
public:
    // one of many virtual functions that deal with IItem CRUD
    virtual IItem* createItem() = 0; 

    // concrete method that works on interfaces
    void doWork()
    {
        IItem* item = createItem();
        // do stuff with an IItem*
    }

};


class Toy : public IItem
{

};

// one of many kinds of workers
class ElfWorker : public IWorker
{
public:

    ElfWorker()
    {
        // constructor implicitly calls IWorker()
    }

    IItem* createItem() override
    {
        return new Toy;
    }
};

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

Так что я думаю, что это тот случай, когда код Go интерфейса ew.ItemCRUD должен быть явно установлен с указателем на ElfWorker.

Эльф знает, как создатьItem (), который в его случае оказывается игрушечным объектом. Другие работники будут реализовывать свой собственный ItemCRUD для своих объектов данных.

type Item interface {                         
    // various methods                                                                                
}
type ItemCRUD interface {                                                                             
    create() Item                                                                                     
    // other CRUD                                                                                     
}                                                                                                     

type Worker struct {
    ItemCRUD  // embedded interface                                                                                        
}                                                                                                     
func (w *Worker) doWork() {
    item := w.create()                                                                                
    // do stuff with item                                                                          
}

type Toy struct {
}

type ElfWorker struct {                                                                               
    Worker // embedded                                                                                
    // ..
}       

func NewElfWorker() *ElfWorker {                                                                      
    ew := &ElfWorker{}                                                                                
    ew.ItemCRUD = ew // <-- #### set Worker ItemCRUD  explicitly ####                                                  
    return ew                                                                                         
}                                                                                                     

func (ew *ElfWorker) createItem() Item {
    return &Toy{}                                                                                     
}
// more ElfWorker Item CRUD
func bigFunction(w *Worker) {                                                                     
    // ...                                                                                            
    w.doWork()  
   // ..                                                                                       
}   

Так что часть, с которой я немного борюсь, это явная настройка. Похоже, что "Go way" композиции требует этого явного шага, если я хочу, чтобы базовый класс Worker предоставлял общие методы для Items.

Мысли?

Ответы [ 2 ]

0 голосов
/ 02 января 2019

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

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

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

0 голосов
/ 02 января 2019

Будучи новичком, чтобы идти сам, этот ответ не подкреплен многолетним опытом работы: -)

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

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

Чтобы предоставить любому эльфийскому объекту реализацию по умолчанию ItemCRUD, вам необходимо реализовать адаптер для ItemCRUD и составить адаптер с вашим абстрактным эльфом. У абстрактных методов может быть реализация по умолчанию как log.Fatal("not implemented")

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

Тем не менее, поскольку у go нет обобщений, может быть неправильным подходом иметь ItemCRUD.

...