dongxiezhi0590 2019-01-02 18:28
浏览 63

Go vs C ++中的抽象基类设计

I am still learning the Go way of doing things, coming from a C++ background. I am looking for feedback contrasting OOP inheritance to interface composition.

I have a design situation in a Go program where, if I was implementing in C++, I would solve with an abstract base class.

Suppose I need a base class, which has many implementors. The base class has shared methods that do work on abstract data items. Different Worker implementations provide CRUD operations on different item types, but workers all use the shared methods of the base class for general work.

In C++ I might do it this way

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;
    }
};

In Go you don't have abstract virtual methods such as IWorker::createItem(). Concrete classes need to supply the base with an interface or function that do the right thing.

So I think it is the case that the Go code the ew.ItemCRUD interface has to be explicitly set with a pointer to an ElfWorker.

The elf knows how to createItem(), which in his case happens to be Toy object. Other workers would implement their own ItemCRUD for their data objects.

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()  
   // ..                                                                                       
}   

So the part that I am wrestling a bit with is explicit setting. Seems like the "Go way" of composition does require this explicit step if I want the base Worker class to provide shared methods on Items.

Thoughts?

  • 写回答

2条回答 默认 最新

  • dpzlz08480 2019-01-02 18:53
    关注

    Beeing new to go myself, this answer is not backed by years of go experience :-)

    I don't know, if the way you tackle this is the correct approach. go allows interfaces to be implemented without explicit declaration. If you have elves, and you need them to do ItemCRUD methods, just implement them.

    The method set will then match the interface and you can use the elf as a ItemCRUD where required.

    To supply any elf object with a default ItemCRUD Implementation, you should implement an adapter for the ItemCRUD and compose the adapter with your abstract elf. The abstract methods could have a default implementation as log.Fatal("not implemented")

    The concrete elves shadow the adapter's methods, this answers your question: It is not required to insert the object during creation.

    Yet, since go has no generics, it may not be the right approach to have an ItemCRUD.

    评论

报告相同问题?

悬赏问题

  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统