扩展计算器以包含复数和有理数模块(使用动态绑定)。

huangapple go评论82阅读模式
英文:

Extend calculator with complex and rational module(using dynamic binding)

问题

我已经制作了一个可以计算整数和实数的计算器(我用Go语言制作的)。
然后我想通过添加这些模块来使其能够计算复数和有理数(它也可以在类型混合的情况下进行计算)。
如果我每次运行时都检查操作数的类型并处理每种情况,那么这可能很容易,但我想用动态绑定来解决这个问题。你们能告诉我如何解决这个问题的思路吗?

英文:

I already made calculator that can compute with integers and real numbers(i made it with go).
Then I want to make it possible to calculate complex and rational numbers by adding those modules.
(it can also calculate when types are mixed)
It can be easy if I check types of operands every time(runtime) and take care of each case, but I want to solve it with dynamic binding. Guys can you tell me the idea of how to solve this problem

答案1

得分: 0

我认为你提到的动态类型,可能是指在C++和Java等语言中,动态绑定实际上是指具有指向派生类的基类引用(因为派生类“是一个”基类)。

可以说基类定义了派生类变形背后的接口。如果派生类替换了基类的方法,那么对基类的引用仍然可以访问派生类中的这些方法。

基类可以定义一些方法,为其派生类提供一些“基本”功能。如果这些类没有重新定义该方法,就可以调用基类的方法。

在Go语言中,这两个概念也存在,但它们是完全不同的。

Go语言有一个显式的interface关键字,用于定义方法签名但不包含方法体。只要具有相同名称和签名的方法,任何值都会隐式满足该接口。

type LivingBeing interface {
    TakeInEnergy()
    ExpelWaste()
}

接口类型在代码中成为有效的类型。我们可以将接口传递给函数,并且在不知道满足该接口的类型的情况下,可以调用其方法:

func DoLife(being LivingBeing) {
   being.TakeInEnergy()
   being.ExpelWaste()
}

这是有效的代码,但不是完整的代码。与其他语言中的基类不同,接口不能定义函数,只能定义函数签名。它纯粹只是一个接口定义。我们必须将满足接口的类型与接口本身分开定义

type Organism struct{}

func (o *Organism) TakeInEnergy() {}

func (o *Organism) ExpelWaste() {}

现在我们有一个满足LivingBeing接口的Organism类型。它有点像一个基类,但如果我们想要构建它,我们不能使用子类化,因为Go语言不支持。但是Go提供了类似的东西,称为嵌入类型

在这个例子中,我将定义一个新的生物体Animal,它从LivingBeing中继承了ExpelWaste(),但定义了自己的TakeInEnergy()

type Animal struct {
	Organism  // 嵌入类型的语法:类型但没有字段名
}

func (a *Animal) TakeInEnergy() {
	fmt.Printf("我是一个动物")
}

从那段代码中并不明显的是,因为AnimalOrganism不是一个命名字段,所以它的字段和方法可以直接从Animal访问。就好像Animal是一个”生物体一样。

然而,它不是一个生物体。它是一个不同的类型,更准确地说,将对象嵌入视为将Organism的字段和方法自动提升到Animal的语法糖。

由于Go是静态和显式类型的,DoLife不能接受一个Organism,然后传递一个Animal:它们的类型不同:

/* 这是不起作用的。Animal嵌入organism,但*不是*organism */
func DoLife(being *Organism) {
   being.TakeInEnergy()
   being.ExpelWaste()
}
func main() {
	var a = &Animal{Organism{}}
	DoLife(&Animal{})
}
无法将&Animal{}类型*Animal作为参数传递给DoLife

这就是为什么存在interface的原因-这样OrganismAnimal(甚至PlantChemotrophBacteria)都可以作为LivingBeing传递。

将所有内容放在一起,这是我一直在使用的代码:

package main

import "fmt"

type LivingBeing interface {
	TakeInEnergy()
	ExpelWaste()
}

type Organism struct{}

func (o *Organism) TakeInEnergy() {
}

func (o *Organism) ExpelWaste() {}

type Animal struct {
	Organism
}

func DoLife(being LivingBeing) {
	being.TakeInEnergy()
	being.ExpelWaste()
}

func (a *Animal) TakeInEnergy() {
	fmt.Printf("我是一个动物")
}

func main() {
	var a = &Animal{Organism{}}
	DoLife(a)
}

有几个注意事项:

  1. 语法上,如果要声明一个嵌入的字面量,必须显式提供其类型。在我的例子中,Organism没有字段,所以没有什么需要声明的,但我仍然将显式类型留在那里,以指导你的方向。

  2. DoLife可以在LivingBeing上调用正确的TakeInEnergy,但Organism的方法不能。它们只能看到嵌入的Organism

func (o *Organism) ExpelWaste() {
	o.getWaste() // 这将始终是Organism的getWaste,而不是Animal的
}

func (o *Organism)getWaste() {}

func (a *Animal)getWaste() {
    fmt.Println("动物的废物")
}

类似地,如果只传递嵌入的部分,那么它将调用自己的TakeInEnergy(),而不是Animal的;没有Animal了!

func main() {
	var a = &Animal{Organism{}}
	DoLife(&a.Organism)
}

总结一下,

  • 显式定义interface,并在需要“多态”行为的地方使用该类型

  • 定义基本类型并将其嵌入到其他类型中以共享基本功能。

  • 不要期望“基类”能够“绑定”到“派生类”的函数。

英文:

I think by dynamic typing, you're probably referring to how in eg C++ and Java, dynamic binding is essentially having reference to a base class that can point to a derived class (because the derived class "is a" base class).

One might say that the base class defines the interface behind which derived classes morph. If the derived classes replace methods of the base class, a reference to the base class can still access those methods in the derived class.

Base class can define some methods to provide some "base" functionality to its derived classes. If those classes don't redefine the method, the base class's method can be called.

In go, both these concepts exist, but they're quite different.

Go has an explicit interface keyword that defines method signatures but no methods. Any value implicitly satisfies that interface if it has methods of the same name with the same signature.

type LivingBeing interface {
    TakeInEnergy()
    ExpelWaste()
}

The interface type becomes a valid type in the code. We can pass an interface to a function, and without knowing the type satisfying that interface, can call its methods:

func DoLife(being LivingBeing) {
   being.TakeInEnergy()
   being.ExpelWaste()
}

This is valid code, but not complete code. Unlike with base classes in other languages, interfaces cannot define functions, only their signatures. It is purely and only an interface definition. We have to define the types that satisfy the interface separately from the interface itself.

type Organism struct{}

func (o *Organism) TakeInEnergy() {}

func (o *Organism) ExpelWaste() {}

We now have a type organism that satisfies LivingBeing. It is something like a base class, but if we wanted to build on it, we can't use subclassing because Go doesn't implement it. But Go does provide something similar called embedding types.

In this example I'll define a new organism, Animal, that draws ExpelWaste() from LivingBeing, but defines its own TakeInEnergy():

type Animal struct {
	Organism  // the syntax for an embedded type: type but no field name
}

func (a *Animal) TakeInEnergy() {
	fmt.Printf("I am an animal")
}

What isn't obvious from that code is that because Animal's Organism is not a named field, its fields and methods are accessible directly from Animal. It's almost as if Animal "is an" organism.

However it is *not * an organism. It is a different type, and it would be more accurate to think of object embedding as syntactic sugar to automatically promote Organism's fields and methods to Animal.

Since go is statically and explicitly typed, DoLife cannot take an Organism and then be passed an Animal: it doesn't have the same type:

/* This does not work.  Animal embeds organism, but *is not* an organism */
func DoLife(being *Organism) {
   being.TakeInEnergy()
   being.ExpelWaste()
}
func main() {
	var a = &Animal{Organism{}}
	DoLife(&Animal{})
}
cannot use &Animal{} (type *Animal) as type *Organism in argument to DoLife

That's why interface exists - so that both Organism and Animal (and indeed, even Plant or ChemotrophBacteria) can be passed as a LivingBeing.

Putting it all together, here's the code I've been working from:

package main

import "fmt"

type LivingBeing interface {
	TakeInEnergy()
	ExpelWaste()
}

type Organism struct{}

func (o *Organism) TakeInEnergy() {
}

func (o *Organism) ExpelWaste() {}

type Animal struct {
	Organism
}

func DoLife(being LivingBeing) {
	being.TakeInEnergy()
	being.ExpelWaste()
}

func (a *Animal) TakeInEnergy() {
	fmt.Printf("I am an animal")
}

func main() {
	var a = &Animal{Organism{}}
	DoLife(a)
}

There are a few caveats:

  1. Syntactically, If you want to declare an embedded literal you must explicitly provide its type. In my example Organism has no fields so there's nothing to declare, but I still left the explicit type in there to point you in the right direction.

  2. DoLife can call the right TakeInEnergy on a LivingBeing, but Organism's methods cannot. They see only the embedded Organism.

func (o *Organism) ExpelWaste() {
	o.getWaste() //this will always be Organism's getWaste, never Animal's
}

func (o *Organism)getWaste() {}

func (a *Animal)getWaste() {
    fmt.Println("Animal waste")
}

Simlarly if you pass only the embedded part, then it's going to call its own TakeInEnergy(), not that of Animal; there's no Animal left!

func main() {
	var a = &Animal{Organism{}}
	DoLife(&a.Organism)
}

In summary,

  • Define explict interface and use that type wherever you want "polymorphic" behavior

  • Define base types and embed them in other types to share base functionality.

  • Don't expect the "base" type to ever "bind" to functions from the "derived" type.

huangapple
  • 本文由 发表于 2021年11月21日 00:12:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/70047674.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定