Golang's interface is a ghost.

Source: Internet
Author: User
Tags cos
This is a creation in Article, where the information may have evolved or changed.

Golang's interface is a ghost.

Problem overview

Golang's interface is different from other languages. It does not require explicit implements, and as long as a struct implements all the functions in interface, the compiler automatically thinks it implements this interface. The first time I saw this design, my first reaction was: What's the fuck? What is the difference between the design of this wonderful flower and the explicit implement or inheritance of the mainstream OO language?

Until after watching the SICP, my views changed: Golang this way and Java, C + + flow is not fundamentally different, are the implementation of polymorphism of the specific way. The so-called polymorphic, is "an interface, a variety of implementations."

SICP explains in detail why the same interface requires different implementations depending on the data type, and how to do that. There is no OO concept, first put oo aside, from the principle of looking at how this is done.

First put the approximate principle here, and then give an example. In order to implement polymorphism, it is necessary to maintain a global lookup table, its function is to return the corresponding function entry according to the type name and the method name. When I have added a type, I need to add the name of the new type, the corresponding method name, and the actual function entry to the table. This is basically called dynamic binding, similar to the vtable in C + +. For the Lisp language used in SICP, these tasks need to be done manually. For Java, the work is done through implements. And Golang in a more radical way, even implements are saved, the compiler automatically discovers automatic binding.

An example of a plural package

SICP in the plural as an example, I used Clojure, Java and Golang, respectively, implemented a bit, the code is placed in the https://github.com/nanoix9/golang-interface. The purpose here is to implement a complex package, which supports both Cartesian (rectangular) and polar coordinates (polar) implementations, but both provide external interfaces in the same form, including the acquisition of real, imaginary, modulo, and angular four operations, for the sake of simplicity, just take the real part for example. There is complete content in the code.

Version Clojure

For rectangular coordinates, it is represented by a list of two elements, which are both real and imaginary parts.

(defn make-rect [r i] (list r i))

For polar coordinates, it is also a list of two elements, representing the modulo and the spoke angles, respectively.

(defn make-polar [abs arg] (list abs arg))

Now add a function to "take the real part" get-real . The problem is, I hope this function can handle two coordinates at the same time, and for the user, the behavior of the function is consistent regardless of which coordinate get-real is used. The simplest idea is to add a tag field to differentiate between the two types and then get-real perform different actions based on the type information.

To do this, define attach-tag , get-tag and get-content function for associating labels, extracting tags, and extracting content:

(defn attach-tag [tag data] (list tag data))(defn get-tag [data-with-tag] (first data-with-tag))(defn get-content [data-with-tag] (second data-with-tag))

Add tag to a function that constructs a complex number

(defn make-rect [r i] (attach-tag 'rect (list r i)))(defn make-polar [abs arg] (attach-tag 'polar (list abs arg)))

get-realThe function obtains the tag first, performs different operations according to the Cartesian or polar coordinates

(defn get-real [c]  (let [tag (get-tag c)        num (get-content c)]    (cond (= tag 'rect) (first num)          (= tag 'polar) (* (first num) (Math/cos (second num)))          :else (println "Unknown complex type:" tag))))

But there is a problem, what if we want to add a third type? Must be modified get-real函数 . In other words, to add an implementation, you must change the main entry of the function. Is there a way to avoid it? The answer is to use the previous lookup table (which is not the only way, of course, and the message delivery method is also described in SICP, which is not covered here). This lookup table provides get-op and put-op two methods

 (defn get-op [tag op-name] ... (defn put-op [tag op-name func] ...)

Only the prototype is given here, and get-op the corresponding function entry is obtained based on the type name and the method name. Instead put-op , add the type name, method name, and function entry to the table. The contents of this table can be intuitively understood.

Tag\op-name ' Get-real ' Get-image ...
' Rect Get-real-rect Get-image-rect ...
' Polar Get-real-polar Get-image-polar ...

The get-real function can be implemented as follows: first, each type adds its own function entry to the lookup table

(defn install-rect []  (letfn [(get-real [c] (first c))]    put-op 'rect 'get-real get-real))(defn install-polar []  (letfn [(get-real [c] (* (first c) (Math/cos (second c))))]    put-op 'polar 'get-real get-real))(install-rect)(install-polar)

Note that the local function is used here letfn , so both types are used get-real as function names and do not conflict with each other.

Defines a apply-generic function that takes a function entry from a lookup table and removes the tag, giving the contents and remaining parameters to the acquired function

(defn apply-generic [op-name tagged-data & args]  (let [tag (get-tag tagged-data)        content (get-content tagged-data)        func (get-op tag op-name)]    (if (null? func)        (println "No entry for data type" tag "and method" op-name))        (apply func (cons content args))))

get-realfunction can implement the

(defn get-real [c]    (apply-generic 'get-real c))

Java Edition

Java implementation of complex packages does not require such trouble, the compiler has done most of the work. Java is, of course, a static language and a type check.

public interface Complex {    public double getReal();    ...}public class ComplexRect implements Complex {    private double real;    private double image;    public double getReal() {        return real;    }    ...}public class ComplexPolar implements Complex {    private double abs;    private double arg;    public double getReal() {        return abs * Math.cos(arg);    }    ...}

Version Golang

The difference between Golang and Java is that it savesimplements

type Complex interface {    GetReal() float64    ...}type ComplexRect struct {    real, image float64}func (c ComplexRect) GetReal() float64 {    return c.real}...type ComplexPolar struct {    abs, arg float64}func (c ComplexPolar) GetReal() float64 {    return c.abs * math.Cos(c.arg)}...

At first glance there is ComplexRect no Complex relationship between it and what it is implied that the compiler automatically discovers. This is more flexible, such as adding a new interface type, and the compiler will automatically discover that the struct implements the interface without modifying the struct's code. If it is Java, you must modify the source code, explicit implements .

Summarize

Through this problem, I realized that OO is just a way, in fact there is no object. As for why Oo, the most fundamental is to achieve "an interface, a variety of implementations", which requires that the interface is stable, and the implementation may be changeable. If the interface is also constantly changing, there is no need to abstract the interface. Whether the code structure reflects the inheritance/composition of the world is not important or fundamental. It is important to separate the stable interfaces from the unstable implementations, so that when a module is changed, it does not affect other parts. This is a requirement of the complexity of the software, and the decomposition and isolation of the module is particularly important for large software.

In order to achieve this, C + + implements the Vtable,java provides the Interface,golang to automatically discover this relationship. You can use OO, or not oo. Regardless of the way the language provides, the idea behind it is unified. We can even implement relevant mechanisms, such as spring, to complete dependency injection through XML, so that we can replace another implementation with one implementation without altering the source code.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.