Какой самый идиоматический способ имитировать наследование в Go в этом конкретном случае? - PullRequest
0 голосов
/ 27 ноября 2018

Я много размышлял об этой конкретной проблеме, и у меня возникло то, как я должен решить ее самым чистым способом.

Представьте себе приложение, похожее на это:

type AreaCalculator interface {
  Area() int
}

type Rectangle struct {
    color  string
    width  int
    height int
}

type (r *Rectangle) Area() int {
   return r.width * r.height
}

type Circle struct {
    color    string
    diameter int
}

type (c *Circle) Area() int {
   return r.diameter / 2 * r.diameter / 2 * π
}

type Canvas struct {
    children []AreaCalculator
}

func (c *Canvas) String() {
    for child := range c.children {
        fmt.Println("Area of child with color ", child.color, " ", child.Area())
    }
}

Этот пример, очевидно, не будет компилироваться, потому что хотя метод String () Canvas может вызывать c.Area (), он не может получить доступc.color, поскольку нет способа убедиться, что структура, реализующая AreaCalculator, обладает этим свойством.

Одним из решений, которое я мог придумать, было сделать это следующим образом:

type AreaCalculator interface {
  Area() int
  Color() string
}

type Rectangle struct {
    color  string
    width  int
    height int
}

type (r *Rectangle) Color() string {
   return r.color
}

type (r *Rectangle) Area() int {
   return r.width * r.height
}

type Circle struct {
    color    string
    diameter int
}

type (c *Circle) Area() int {
   return r.diameter / 2 * r.diameter / 2 * π
}
type (c *Circle) Color() string {
   return c.color
}

type Canvas struct {
    children []AreaCalculator
}

func (c *Canvas) String() {
    for child := range c.children {
        fmt.Println("Area of child with color ", child.Color(), " ", child.Area())
    }
}

Другим способом было бы попробовать что-то вроде этого:

type Shape struct {
    Area func() int 
    color string
    diameter int
    width int
    height int
}

func NewCircle() Shape {
    // Shape initialisation to represent a Circle. Setting Area func here
}


func NewRectangle() Shape {
    // Shape initialisation to represent a Rectangle. Setting Area func here
}

type Canvas struct {
    children []Shape
}

func (c *Canvas) String() {
    for child := range c.children {
        fmt.Println("Area of child with color", child.color, " ", child.Area())
    }
}

Ни одно изэти варианты кажутся мне чистыми.Я уверен, что есть более чистое решение, о котором я не могу думать.

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Важной отправной точкой является то, что вы не должны имитировать наследование в Go.Иди не имеет наследства.Он имеет интерфейсы и имеет встраивание.Они не забыли включить наследство; намеренно не является частью языка .Go поощряет композицию.

Твоему Canvas нужно больше, чем AreaCalculator.Нужно что-то, что обеспечивает цвет.Вы должны выразить это.Например, вы могли бы сделать это:

type DrawableShape interface {
  AreaCalculator
  Color() string
}

И тогда вы бы реализовали Color() для Rectangle и Circle.

func (r Rectangle) Color() string {
  return r.color
}

func (c Circle) Color() string {
  return c.color
}

И children будет []DrawableShape:

children []DrawableShape

Это оставило бы что-то вроде this (построение кода Мухаммеда Насирифара).

package main

import (
    "fmt"
    "math"
    "strings"
)

type AreaCalculator interface {
    Area() int
}

type DrawableShape interface {
  AreaCalculator
  Color() string
}

type Rectangle struct {
    color  string
    width  int
    height int
}

func (r Rectangle) Area() int {
    return r.width * r.height
}

func (r Rectangle) Color() string {
  return r.color
}

type Circle struct {
    color    string
    diameter int
}

func (c Circle) Area() int {
    area := math.Round(float64(c.diameter*c.diameter) * math.Pi / float64(4))
    return int(area)
}

func (c Circle) Color() string {
  return c.color
}

type Canvas struct {
    children []DrawableShape
}

func (c Canvas) String() string {
    lines := make([]string, 0)
    for _, child := range c.children {
        lines = append(lines, fmt.Sprintf("Area of child with color %s %d", child.Color(), child.Area()))
    }
    return strings.Join(lines, "\n")
}

func main() {
    circle := &Circle{color: "red", diameter: 2}
    rect := &Rectangle{color: "blue", width: 3, height: 4}

    canvas := &Canvas{
        children: []DrawableShape{circle, rect},
    }

    fmt.Println(canvas.String())
}
0 голосов
/ 28 ноября 2018

Ключевое замечание здесь: если вам нужна определенная функциональность, сделайте ее явной.Также не выполняйте работу других объектов от их имени.

Также обратите внимание, что String() должен возвращать строку, а не записывать в stdout.

package main

import (
    "fmt"
    "math"
    "strings"
)

type AreaCalculator interface {
    fmt.Stringer
    Area() int
}

type Rectangle struct {
    color  string
    width  int
    height int
}

func (r *Rectangle) Area() int {
    return r.width * r.height
}

func (r *Rectangle) String() string {
    return fmt.Sprintf("I'm a rectangle %d", r.width)
}

type Circle struct {
    color    string
    diameter int
}

func (c *Circle) Area() int {
    area := math.Round(float64(c.diameter*c.diameter) * math.Pi / float64(4))
    return int(area)
}

func (c *Circle) String() string {
    return fmt.Sprintf("I'm a circle: %d", c.diameter)
}

type Canvas struct {
    children []AreaCalculator
}

func (c *Canvas) String() string {
    lines := make([]string, 0)
    for _, child := range c.children {
        lines = append(lines, child.String())
    }
    return strings.Join(lines, "\n")
}

func main() {
    circle := &Circle{color: "red", diameter: 2}
    rect := &Rectangle{color: "blue", width: 3, height: 4}

    canvas := &Canvas{
        children: []AreaCalculator{circle, rect},
    }

    fmt.Println(canvas.String())
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...