Golang初级系列教程-接口2

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一种神奇的语言,让我们一起进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值