热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Golang初级系列教程-接口2

Golang初级系列教程-接口2在上一篇文章中,已经通过一个典型的OOP的例子讲述了Go中接口的概念。Bob在Google论坛上指出:Go接

Golang初级系列教程-接口2

在上一篇文章中,已经通过一个典型的 OOP 的例子讲述了 Go 中接口的概念。Bob 在Google论坛上指出:Go 接口比 Java 或者 C# 的更加灵活多变,更加适用于大型编程,是一项跨时代的设计。(“Go’s interfaces aren’t a variant on Java or C# interfaces, they’re much more. They are a key to large-scale programming and adaptable, evolutionary design.” )

首先,通过 Java 接口对比,从而揭示这种革新的变化。让我们通过 Bus 来讲述,一辆 Bus 可以认为实现两个接口——框架可以用来计算体积和交通工具用来载客。

Java 代码

//OOP Step 1: design your interface and class hierarchy

//OOP Step 1.1: Pre-define what real-world abstractions could use the data we will define
interface PublicTransport {
int PassengerCapacity();
}

//OOP Step - repeat for any other interfaces
interface Cuboid {
int CubicVolume();
}

//OOP Step 2: Create data structures and implement all interfaces we have already defined in our class hierarchy
public class Bus implements
PublicTransport,
Cuboid {


//OOP Step 2.1: Define data structures for class
int l, b, h, rows, seatsPerRow;

public Bus(int l, int b, int h, int rows, int seatsPerRow) {
this.l = l; this.b = b; this.h = h; this.rows = rows; this.seatsPerRow = seatsPerRow;
}

//OOP Step 2.2: Define method implementation
public int CubicVolume() { return l*b*h; }

public int PassengerCapacity() { return rows * seatsPerRow; }

//OOP Step 3: Use the classes and methods in main program
public static void main() {
Bus b = new Bus(10, 6, 3, 10, 5);
System.out.Println(b.CubicVolume());
System.out.Println(b.PassengerCapacity());
}
}

注意:上面 Java 代码中,必须通过 implements 关键字,描述类之间的关系,在 C# 中则需要使用 : 代替 implements。这种情况下,如果类之间的关系更新了,不得不修改这些核心的模块。

大多数项目,都有一个长期的架构和设计时期,在这期间需要不断的更新接口和数据结构,直到所有的因素都考虑到,然而这就需要不断的更新核心代码。一旦设计结构最终定好之后,如果没有太大的改动理由,是不会去更改类之间的层次结构的。在我工作过的项目中,架构是由专业的架构师负责,如果不是评审委员会同意架构是不会产生变化的。上游模块的改动往往导致下游模块也跟着一起改动。另一种方案,是重新封装现有的类,生成新的接口,但这往往又会造成系统庞大,难以管理。为了解决复杂性和模块的变化,设计模式应运而生。我承认,我非常喜欢设计模式和面向对象的编程过程。但是,设计模式很多,需要我们必须有很好的专业知识,也需要一个很长的学习过程。与其花费更多的设计时间,Go 可否能提供更直观简单的方式呢?

接下来,让我们把上面的代码用 Go 实现。

package main

import "fmt"

//Go Step 1: Define your data structures
type Bus struct {
l, b, h int
rows, seatsPerRow int
}

//Go Step 2: Define a real world abstraction that could use the data we structure we have
type Cuboider interface {
CubicVolume() int
}

//Go Step 3: Implement methods to work on data
func (bus Bus) CubicVolume() int {
return bus.l * bus.b * bus.h
}

//Go step - repeat 2 & 3 for any other interfaces
type PublicTransporter interface {
PassengerCapacity() int
}

func (bus Bus) PassengerCapacity() int {
return bus.rows * bus.seatsPerRow
}

func main() {
b := Bus{
l:10, b:6, h:3,
rows:10, seatsPerRow:5}

fmt.Println("Cubic volume of bus:", b.CubicVolume())
fmt.Println("Maximum number of passengers:", b.PassengerCapacity())
}

类型变量和结构名完全相同,但是仍有些许区别。其中之一,便是不需要像 Java 中的关键字 implements 去声明类之间层次结构。另外,Go 以数据为中心——首先定义数据结构,之后根据需要定义接口。层次结构在 Go 中表现为一种共识,实现相同的方法即可认为是属于同一种接口定义。在上面的例子中,由于实现的功能相同,两种设计的差异不突出,但是随着程序不断的添加新功能,Go 的特性就会显现出来。

假设当前需求变化,要求每一辆 Bus 必须保证每名乘客都至少有一定的空间。 Bus 必须实现另一个接口 PersonalSpaceLaw,在 Java 或者其它语言中,我们可能进行如下的代码设计:

//new requirement that the Bus must be compatible with
interface PersonalSpaceLaw {
boolean IsCompliantWithLaw();
}

class Bus implements
PublicTransport,
Cuboid,
PersonalSpaceLaw {

//... other existing code

public IsCompliantWithLaw()
{
return ( l * b * h ) / ( rows * seatsPerRow ) > 3;
}
}

上述代码中,改变了类之间的层次关系,导致我们不得不修改核心代码,然而这就产生了问题。如果核心代码已经很长时间没有变更或者已经成型,为了添加这个功能我们不得不说服委员会去更新设计结构,所以我们需要准备洽谈、文档、开会等等等。

让我们看看 Go 能做什么吧

//new requirement that the Bus must be compatible with
type PersonalSpaceLaw interface {
IsCompliantWithLaw() bool
}

func (b Bus) IsCompliantWithLaw() bool {
return (b.l * b.b * b.h) / (b.rows * b.seatsPerRow) >= 3
}

看到没,之前的代码一点没改,就完成了结构的扩展。是不是更加干净、灵活,对新需求更加容易扩展。

引用 John Asmuth 发表在论坛上的话作为总结:不必提前花费时间设计并多次修整类结构;根本无需关心这些,随着编码的进展,算法结构已经符合实际情况。

注意:在这篇文章中,并没有贬低或者不重视优秀、简洁的设计,毕竟时间在前进,未来的需求不可能完全考虑到。

完整代码

package main

import "fmt"

type Bus struct {
l, b, h int
rows, seatsPerRow int
}

type Cuboider interface {
CubicVolume() int
}

func (b Bus) CubicVolume() int {
return b.l * b.b * b.h
}

type PublicTransporter interface {
PassengerCapacity() int
}

func (b Bus) PassengerCapacity() int {
return b.rows * b.seatsPerRow
}

func main() {
b := Bus{
l:10, b:6, h:3,
rows:10, seatsPerRow:5}

fmt.Println("Cubic volume of b:", b.CubicVolume())
fmt.Println("Maximum number of passengers:", b.PassengerCapacity())
fmt.Println("Is compliant with law:", b.IsCompliantWithLaw())
}

type PersonalSpaceLaw interface {
IsCompliantWithLaw() bool
}

func (b Bus) IsCompliantWithLaw() bool {
return (b.l * b.b * b.h) / (b.rows * b.seatsPerRow) >= 3
}

Golang一种神奇的语言,让我们一起进步


推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • IB 物理真题解析:比潜热、理想气体的应用
    本文是对2017年IB物理试卷paper 2中一道涉及比潜热、理想气体和功率的大题进行解析。题目涉及液氧蒸发成氧气的过程,讲解了液氧和氧气分子的结构以及蒸发后分子之间的作用力变化。同时,文章也给出了解题技巧,建议根据得分点的数量来合理分配答题时间。最后,文章提供了答案解析,标注了每个得分点的位置。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
author-avatar
蒋筱谷_680
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有