C、C++、golangのいくつかの言語インタフェースに対する理解

8876 ワード

以前はCとC++しか使わなかったが,インタフェースという概念については論理的な認識が1つしか存在せず,コードライターと使用者を接続するチャネルであると考えられていたが,現在は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.
", tar->name, tool->name); return; } printf("Peel %s with %s. take %d seconds.
", 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.
", tar->name, tool->name); return; } printf("Peel %s with %s. take %d seconds.
", 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.
", tar->name, tool->name); return; } printf("Peel %s with %s. take %d seconds.
", 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.
", tar->name, tool->name); return; } printf("Peel %s with %s. take %d seconds.
", 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つのデータ型と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
", 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
", 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
", 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.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関数パラメータはすべて値で渡されるため、外部のリアルオブジェクトに影響を与えることはできません.
 
まとめ
インタフェースは1種の規範で、1つのプロトコルで、1つの抽象的な方法の集合で、単純にそれ自身の語の意味の上で、上述のいくつかの言語は高下がなくて、たとえ彼らが対象と過程に向かう違いがあっても、しかし“需要の変化に耐えるインタフェース”はプログラミング言語にもっと合理的なインタフェースの設計があることを要求して、この範疇の内で、個人的には、C言語のインタフェースが最も簡単で、その言語レベルで提供される拡張性が低いため、理解しやすいように見えますが、インタフェースを拡張する必要がある場合は、これは極めて苦痛な過程です.C++は継承と虚関数を通じてインタフェースの拡張性を大幅に向上させ、コードは簡潔明瞭で、明示的な宣言が必要であることを前提としている.Goは直接言語レベルでインタフェースの設計をサポートし、インタフェースを使用するプロセスが極めて簡略化されています.もしあなたのクラスがインタフェース内のメソッドセットを完全に実現したら、あなたはインタフェースを実現することに相当します.両者の定義プロセスが関連していなくても、これはいわゆる「非侵入インタフェース」です.内中の意味はゆっくりと体得しなければなりません~