c语言借口,对C、C++、golang几种语言接口的理解

之前只用过C和C++,对于接口这个概念只存在一个逻辑上的认识,认为它是连接代码写作方和使用方的一个渠道,现在接触到了GO语言,接口(interface{})是GO的一个重要特性,因此决定对C、C++和GO的接口特性进行一番梳理和对比。

C语言

C语言的接口概念较为简单,在只讨论C语言基础用法(不掺杂模拟面向对象的用法)的情况下,其接口与使用方的耦合性最高,如果用户想要调用某块功能代码,通常需要通过包含头文件去获取接口类型。接口使用方不需要关心接口的具体实现,只要在保证接口不变(头文件内的部分)的前提下我们可以自由地修改接口实现代码。C就是一把么得感情的尖刀,他为你提供相当具体的方法,使用方法单一,可扩展性较差(好像有点偏题。。。不过就权当又讨论了一番面向过程和面向对象的区别好了)。举个例子,我们现在需要实现一个使用不同工具去为不同食物剥皮的程序:

#include //接口

typedef struct apple { //苹果

//

const char* name;

int size;

}apple;

typedef struct nut { //坚果

//

const char* name;

int size;

}nut;

typedef struct hammer {

const char* name;

int power; //力量度

int sharpness; //锋利度

}hammer;

typedef struct knife {

const char* name;

int power;

int sharpness;

}knife;

void peelApple_hammer(apple* tar, hammer* tool){

if(!tar || !tool){

return;

}

if (tool->sharpness == 0){

printf("Peel %s by %s Failed.\n", tar->name, tool->name);

return;

}

printf("Peel %s with %s. take %d seconds.\n", tar->name, tool->name, tar->size/tool->sharpness);

return;

}

void peelApple_knife(apple* tar, knife* tool){

if(!tar || !tool){

return;

}

if (tool->sharpness == 0){

printf("Peel %s by %s Failed.\n", tar->name, tool->name);

return;

}

printf("Peel %s with %s. take %d seconds.\n", tar->name, tool->name, tar->size/tool->sharpness);

return;

}

void peelNut_hammer(nut* tar, hammer* tool){

if(!tar || !tool){

return;

}

if (tool->power == 0){

printf("Peel %s by %s Failed.\n", tar->name, tool->name);

return;

}

printf("Peel %s with %s. take %d seconds.\n", tar->name, tool->name, tar->size/tool->power);

return;

}

void peelNut_knife(nut* tar, knife* tool){

if(!tar || !tool){

return;

}

if (tool->power == 0){

printf("Peel %s by %s Failed.\n", tar->name, tool->name);

return;

}

printf("Peel %s with %s. take %d seconds.\n", tar->name, tool->name, tar->size/tool->power);

return;

}

外部程序

int main(){

struct apple strap = {"Apple", 10};

struct nut strnut = {"Nut", 5};

struct hammer strham = {"hammer", 5, 0};

struct knife strkni = {"knife", 1, 5};

peelApple_hammer(&strap, &strham);

peelApple_knife(&strap, &strkni);

peelNut_hammer(&strnut, &strham);

peelNut_knife(&strnut, &strkni);

return 0;

}

打印:

Peel Apple by hammer Failed.

Peel Apple with knife. take 2 seconds.

Peel Nut with hammer. take 1 seconds.

Peel Nut with knife. take 5 seconds.

此时,如果我们想要增加一种水果或一种剥皮工具,就至少需要增加一个数据类型和1*n(当前水果或工具的种类数)个函数接口。

C++

C++的接口可被称之为抽象类,即设定一个抽象基类去描述一组基本行为(接口),然后通过多样化的派生类去完成该接口。接口作为不同组件之间的契约存在,且对契约的实现是强制的,即语言必须声明实现了该接口。

#include class fruit;

接口

class toolsak {

public:

toolsak(const char* na, int pow, int shar){

name = na;

power = pow;

sharpness = shar;

}

const char* name;

int power;

int sharpness;

};

class fruit {

public:

fruit(const char* na, int siz){

name = na;

size = siz;

}

const char* name;

int size;

public:

virtual void peel(toolsak* tool) = 0;

};

class hammer : public toolsak {

public:

hammer(const char* name, int power, int sharp):toolsak(name, power, sharp){}

};

class knife : public toolsak {

public:

knife(const char* name, int power, int sharp):toolsak(name, power, sharp){}

};

class apple : public fruit {

public:

apple(const char* name, int size):fruit(name, size){}

void peel(toolsak* tool){

if(!tool || tool->sharpness == 0){

return;

}

printf("Peel the %s with %s. take %d seconds\n", name, tool->name, size/tool->sharpness);

}

};

class nut : public fruit {

public:

nut(const char* name, int size):fruit(name, size){}

void peel(toolsak* tool){

if(!tool || tool->power == 0){

return;

}

printf("Peel the %s with %s. take %d seconds\n", name, tool->name, size/tool->power);

}

};

外部程序

int main(){

toolsak* tptr = new hammer("hammer", 5, 0);

fruit* fptr = new apple("apple", 10);

toolsak* tptr2 = new knife("knife", 1, 5);

fruit* fptr2 = new nut("nut", 5);

fptr->peel(tptr);

fptr->peel(tptr2);

fptr2->peel(tptr);

fptr2->peel(tptr2);

return 0;

}

打印:

Peel the apple with knife. take 2 seconds

Peel the nut with hammer. take 1 seconds

Peel the nut with knife. take 5 seconds

golang

实现类和抽象接口之间不需要硬性连接(即声明继承或虚函数),只要你的实现类实现了某接口规定的方法,那么该类的使用方就可以实例化该类并赋值给接口,然后可以通过接口直接调用具体方法。

package main

import (

"errors"

"fmt"

)

type tool struct {

name string

power int

sharpness int

}

type fruit struct {

name string

size int

}

type toolIf interface {

getName() string

getPower() int

getSharpness() int

}

type fruitIf interface {

peel(too interface{toolIf}) error

}

type hammer struct {

*tool

}

type knife struct {

*tool

}

type apple struct {

*fruit

}

type nut struct {

*fruit

}

func (a *apple) peel(too interface {toolIf}) error {

if too.getSharpness() == 0 {

return errors.New("xxxx")

}

//do that

fmt.Printf("Peel the %v with %v, take %v seconds\n", a.name, too.getName(), a.size/too.getSharpness())

return nil

}

func (n *nut) peel(too interface{toolIf}) error {

if too.getPower() == 0 {

return errors.New("xxxx")

}

//do that

fmt.Printf("Peel the %v with %v, take %v seconds\n", n.name, too.getName(), n.size/too.getPower())

return nil

}

func (t *tool) getName() string {

return t.name

}

func (t *tool) getPower() int {

return t.power

}

func (t *tool) getSharpness() int {

return t.sharpness

}

func main() {

var (

appFr fruitIf = &apple{&fruit{"apple", 10}}

appNu fruitIf = &nut{&fruit{"nut", 5}}

toolHam = &hammer{&tool{"hammer", 5, 0}}

toolKni = &knife{&tool{"knife", 1, 5}}

)

appFr.peel(toolHam)

appFr.peel(toolKni)

appNu.peel(toolHam)

appNu.peel(toolKni)

return

}

打印:

Peel the apple with knife, take 2 seconds

Peel the nut with hammer, take 1 seconds

Peel the nut with knife, take 5 seconds

tips :需要注意,在GO语言中,使用实例对接口赋值最好用指针而不用对象本身,不然有可能编译报错:因为你可以为某个类型或类型指针去定义方法,如:

type fruitIf interface {

getsize() error

getname() error

}

type apple struct{}

func (a apple) getsize() error {

return nil

}

func (a *apple) getname() error {

return nil

}

func main () {

//var appl apple

var frIf fruitIf = &apple{}

frIf.getsize()

}

如上代码编译可以通过,是因为Go可以根据函数1⃣去自动生成一个新方法:

func (a *apple)getsize() error {

return *a.getsize()

}

显然,GO为我们自动做了封装,反之则不然:

ttype fruitIf interface {

getsize() error

}

type apple struct{}

func (a apple) getsize() error {

return nil

}

func (a *apple) getname() error {

return nil

}

func main () {

var appl apple

var frIf fruitIf = appl

frIf.getsize()

}

这样子是不能编译通过的,是因为GO不能根据对象为你生成以下新方法:

func (a apple)getname()error {

return &a.getname()

}

因为GO函数参数全都是按值传递的,为其做操作不能对外部真实对象造成影响。

总结

接口是一种规范,一个协议,一个抽象出来的方法集合,单纯它本身词语的含义上,上述几种语言并无高下,即使他们存在面向对象和面向过程的区别,但是“可以承受需求变化的接口”就要求编程语言有更合理的接口设计,在这个范畴内,个人认为:虽然C语言的接口最为简单,因其语言层面提供的可扩展性较低,显得更容易理解,可是如果需要扩展接口,那么这将是一个极为痛苦的过程;C++通过继承和虚函数大大提升了接口的可扩展性,代码简洁明了,前提是需要显式声明;Go直接在语言层面支持接口的设计,并且使用接口的过程被极大简化,如果你的某个类完全实现了某接口内的方法集,那么你就相当于实现了该接口,即使二者的定义过程并无关联,这就是所谓的“非侵入式接口”。内中含义需慢慢体会~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值