【问题标题】:Abstract base class design in Go vs C++Go vs C++ 中的抽象基类设计
【发布时间】:2019-05-29 09:32:44
【问题描述】:

我仍在学习 Go 的做事方式,来自 C++ 背景。我正在寻找将 OOP 继承与接口组合进行对比的反馈。

我有一个 Go 程序的设计情况,如果我在 C++ 中实现,我会使用抽象基类来解决。

假设我需要一个基类,它有很多实现者。基类具有对抽象数据项起作用的共享方法。不同的 Worker 实现提供了对不同项目类型的 CRUD 操作,但 Worker 都使用基类的共享方法进行一般工作。

在 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()。具体的类需要为基础提供一个接口或函数来做正确的事情。

所以我认为,必须使用指向 ElfWorker 的指针显式设置 ew.ItemCRUD 接口的 Go 代码。

精灵知道如何创建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()  
   // ..                                                                                       
}   

所以我有点纠结的部分是显式设置。如果我希望基础 Worker 类在 Items 上提供共享方法,那么组合的“Go way”似乎确实需要这个 explicit 步骤。

想法?

【问题讨论】:

  • 在 C++ 和任何其他语言之间进行比较可能会适得其反。
  • @Ron 评论的一阶导数:将其他编程语言中的模式应用于 Go 可能会适得其反。
  • 我投票结束这个问题,因为它应该被移到 Codereview
  • 因为我要求对设计选择提供反馈而投反对票?我想我明确表示我想学习?我预先说明了语言会以不同的方式解决问题。

标签: go virtual composition


【解决方案1】:

我并不完全清楚上述代码的计划是什么,并且不理解很难提出具体的解决方案,但很清楚的是,你非常喜欢带着既定的 OOP 思维方式参加聚会(我也这样做了),即很少有助于在 golang 中找到最佳解决方案。

在 Golang 中,我通常不会在实现中嵌入接口,接口在 Golang 中隐式得到满足,这允许很好地分离期望和实现,这通常应该得到尊重。 接收者方法应该期望一个接口,在运行时传递的实现应该只是隐式地满足该接口的签名。

因此,也许我的 doWork 方法需要能够创建项目,那么 doWork 方法将接受它可以调用以创建项目的 ItemCRUD 的任何实现。但这是我在猜测您在这里真正想做什么,我怀疑如果您只是将实现与接口分开,您可能会回答您自己的问题。

【讨论】:

    【解决方案2】:

    我是新手,这个答案没有多年的经验支持:-)

    我不知道,你解决这个问题的方式是否正确。 go 允许在没有显式声明的情况下实现接口。如果您有精灵,并且需要他们执行ItemCRUD 方法,只需实现它们即可。

    然后方法集将与接口匹配,您可以在需要时将 elf 用作ItemCRUD

    要为任何精灵对象提供默认的ItemCRUD 实现,您应该为ItemCRUD 实现一个适配器,并用您的抽象精灵组成适配器。抽象方法可以有一个默认实现为log.Fatal("not implemented")

    具体精灵隐藏适配器的方法,这回答了您的问题:在创建期间不需要插入对象。

    然而,由于 go 没有泛型,因此拥有 ItemCRUD 可能不是正确的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-11
      • 1970-01-01
      • 1970-01-01
      • 2010-12-12
      • 1970-01-01
      • 2011-06-12
      相关资源
      最近更新 更多