Go语言的面向对象模型初探-创新互联

Go语言的面向对象模型与主流OO语言差异很大,本文通过对比Go与C++的三个差异来介绍Go的面向对象模型及其设计思想。

创新互联建站服务项目包括玉泉网站建设、玉泉网站制作、玉泉网页制作以及玉泉网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,玉泉网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到玉泉省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

一:可见性控制粒度是包

Go用首写字母的大小写来控制类、类成员、函数的可见性, 可见性控制的粒度是包。下面是Go和C++对Person的实现:

Go:

type Person struct {

   name string  //首字母小写,包外不可见

   Age  int    //首字母大写,包外可见

}

C++:

struct Person{

   private: std::string name;  //类外不可见

   public: int age;        //类外可见

};

要理解Go的做法,首先我们要问为什么要控制可见性?是为了隐藏实现细节。那么为什么要隐藏实现细节?是为了尽可能地减少对客户代码的影响。说到底是为了让客户能更容易地重用代码,所以可见性控制粒度应该与重用粒度相一致。Go认为重用粒度是包,因此只控制包的可见性。当然从完美角度看,这种做法会增加包内类之间的耦合,但是它也避免了新增友元特性以支持包内类之间的更密切的关联,这使得语言特性保持简洁, 而简洁正是Go语言的追求目标。

二:没有继承,只有组合

Go没有继承,只有组合。类功能复用可以通过匿名组合实现。下面是Go和C++对Teacher的实现:

Go:

type Teacher struct{

   Person

   school string

}

C++:

struct Teacher:Person{

   private: std::string school;

};

继承曾经被认为是OO最重要的特性。随着OO实践的深入,社区才逐渐认识到继承的弊端。实际上,当我们深入研究继承,就会发现它同时干了两件事情:

1、复用实现。

2、IS-A语义。

对于第一点,使用组合远比继承要更优秀,因为组合是黑盒复用,继承是白盒复用,复杂的继承树大大加重了程序员的心智负担。

对于第二点,IS-A语义的威力只有当我们基于接口进行编程(把IS-A理解为接口)时,才能充分地发挥。但是接口本质上是一种抽象,而这种抽象依赖于client,也就是说如果用继承,我们被迫要在实现类的时候对client的使用做适当地预测,否则就很难实现ISP,DIP这些设计原则。

既然继承做了两件事,而且做的都不好,Go就把继承拆分为两个更加单一的特性:匿名组合、Interface。通过匿名组合来复用实现,通过Interface支持基于接口的编程。

三:类型安全的鸭子类型

在第二节我们提到Go没有继承,Go也没有虚函数,它通过Interface实现IS-A语义来支持基于接口的编程,下面用Go和C++分别实现鸭子、野鸭子、打飞鸟的示例:

Go:

type Duck struct {//鸭子

      location Location //鸭子当前位置

}

func (duck *Duck) GetLocation() Location {//获取鸭子当前所处位置

      return duck.location

}

type WildDuck struct {//野鸭子

      Duck

}

func (wildDuck *WildDuck) Fly() {//飞走

}

type Flyer interface {//飞鸟

      Fly()

      GetLocation() Location

}

func ShotFlyer(location Location, flyer Flyer) {//打飞鸟

      if location != flyer.GetLocation() { //没打中飞走了

             flyer.Fly()

      }

}

func TestShotFlyer() {

      flyer := new(WildDuck)

      ShotFlyer(Location{1, 2, 3}, flyer)

}

C++:

struct Flyer{//飞鸟

   virtual void Fly()=0;

   virtual const Location& GetLocation()=0;

};

struct Duck{//鸭子

   void const Location& GetDuckLocation()

   private: Location location;

};

struct WildDuck:Duck, Flyer{//野鸭子

   private: virtual void Fly(){}

   private: virtual const Location& GetLocation(){return GetDuckLocation();}

};

void ShotFlyer(const Location& location, Flyer &flyer) {//打野鸭子

      if (location != flyer.GetLocation()) {//没打中飞走了

             flyer.Fly()

      }

}

void TestShotFlyer() {

      Flyer* flyer := new WildDuck()

      ShotFlyer(Location(1,2,3), flyer)

}

我们先来看Go的实现,WildDuck通过匿名组合Duck来复用Duck的GetLocation方法,为了能让ShotFlyer基于Flyer接口编程,WildDuck并不需要继承Flyer,只要实现了Flyer的所有方法,就能让编译器认为它就是Flyer,Flyer与WildDuck之间是松耦合的关系。

再看C++实现,WildDuck需要继承Flyer接口让编译器认为它就是Flyer,但是WildDuck不能直接复用Duck来实现Flyer的GetLocation方法,因为在编译器看来Duck不是Flyer,那么它就不能实现Flyer的方法,所以WildDuck只能自己实现虚函数GetLocation,通过GetLocation调用Duck的GetDuckLocation来复用Duck的获取当前位置功能。

从上面比较可以看出,Go的实现比C++的更加优雅,这种优雅是由于接口与实现的松耦合带来的。松耦合可以让接口与实现相对独立地演进;可以各自通过组合实现功能复用;也可以在实现具体类之后,无需修改具体类就能新增抽象接口以应对不同的应用场景(这个正是人解决问题的常用方式,先具体再抽象)。

创新互联www.cdcxhl.cn,专业提供香港、美国云服务器,动态BGP最优骨干路由自动选择,持续稳定高效的网络助力业务部署。公司持有工信部办法的idc、isp许可证, 机房独有T级流量清洗系统配攻击溯源,准确进行流量调度,确保服务器高可用性。佳节活动现已开启,新人活动云服务器买多久送多久。


当前文章:Go语言的面向对象模型初探-创新互联
URL地址:http://ybzwz.com/article/ceishj.html