dqhr76378 2017-01-24 02:28
浏览 35
已采纳

Go中是否存在脆弱的基类问题?

Despite using composition over inheritance?

If so, is there any solution for it at the language level?

  • 写回答

2条回答 默认 最新

  • dongxing2302 2017-01-24 11:58
    关注

    As VonC wrote, but I'd like to point out something.

    The fragile base class problem is often blamed on virtual methods (dynamic dispatch of methods – this means if methods can be overridden, the actual implementation that has to be called in case of such an overridden method can only be decided at runtime).

    Why is this a problem? You have a class, you add some methods to it, and if MethodA() calls MethodB(), you can't have any guarantee that the MethodB() you wrote will be called and not some other method of a subclass that overrides your MethodB().

    In Go there is embedding, but there is no polymorphism. If you embed a type in a struct, all the methods of the embedded type get promoted and will be in the method set of the wrapper struct type. But you can't "override" the promoted methods. Sure, you can add your own method with the same name, and calling a method by that name on the wrapper struct will invoke your method, but if this method is called from the embedded type, that will not be dispatched to your method, it will still call the "original" method that was defined to the embedded type.

    So because of this, I'd say the fragile base class problem is only present in a quite mitigated form in Go.

    Example

    Demonstrating the problem in Java

    Let's see an example. First in Java, because Java "suffers" from this kind of problem. Let's create a simple Counter class and a MyCounter subclass:

    class Counter {
        int value;
    
        void inc() {
            value++;
        }
    
        void incBy(int n) {
            value += n;
        }
    }
    
    class MyCounter extends Counter {
        void inc() {
            incBy(1);
        }
    }
    

    Instantiating and using MyCounter:

    MyCounter m = new MyCounter();
    m.inc();
    System.out.println(m.value);
    m.incBy(2);
    System.out.println(m.value);
    

    The output is as expected:

    1
    3
    

    So far so good. Now if the base class, Counter.incBy() would be changed to this:

    void incBy(int n) {
        for (; n > 0; n--) {
            inc();
        }
    }
    

    The base class Counter still remains flawless and operational. But the MyCounter becomes malfunctioning: MyCounter.inc() calls Counter.incBy(), which calls inc() but due to dynamic dispatch, it will call MyCounter.inc()... yes... endless loop. Stack overflow error.

    Demonstrating the lack of the problem in Go

    Now let's see the same example, this time written in Go:

    type Counter struct {
        value int
    }
    
    func (c *Counter) Inc() {
        c.value++
    }
    
    func (c *Counter) IncBy(n int) {
        c.value += n
    }
    
    type MyCounter struct {
        Counter
    }
    
    func (m *MyCounter) Inc() {
        m.IncBy(1)
    }
    

    Testing it:

    m := &MyCounter{}
    m.Inc()
    fmt.Println(m.value)
    m.IncBy(2)
    fmt.Println(m.value)
    

    Output is as expected (try it on the Go Playground):

    1
    3
    

    Now let's change Counter.Inc() the same way we did in the Java example:

    func (c *Counter) IncBy(n int) {
        for ; n > 0; n-- {
            c.Inc()
        }
    }
    

    It runs perfectly, the output is the same. Try it on the Go Playground.

    What happens here is that MyCounter.Inc() will call Counter.IncBy() which will call Inc(), but this Inc() will be Counter.Inc(), so no endless loop here. Counter doesn't even know about MyCounter, it does not have any reference to the embedder MyCounter value.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 matlab有关常微分方程的问题求解决
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算
  • ¥15 java如何提取出pdf里的文字?
  • ¥100 求三轴之间相互配合画圆以及直线的算法
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable