V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GodOfParentheses
V2EX  ›  编程

这种嵌套的继承关系该如何实现?

  •  
  •   GodOfParentheses · 2023-12-14 14:59:49 +08:00 · 549 次点击
    这是一个创建于 370 天前的主题,其中的信息可能已经有所发展或是发生改变。

    B 类继承自 A 类, β类继承自α类. 同时α作为 A 类的字段, β作为 B 类的字段. 现希望 B 实例调用父类方法(该父类方法调用了字段α)时, 能使用自身环境中的β实例而非α实例(因为α对 B 没有意义, 虽然从父类继承, 但完全不使用, 只使用β)

    于是考虑将字段α设为 virtual:

    
    public class Alpha
    {
        public virtual void printSelf()
        {
        	print("this is alpha");
        }
    }
    
    public class Beta: Alpha
    {
        public override void printSelf()
        {
        	print("this is beta");
        }
    }
    
    public class A
    {
        public virtual Alpha alpha
        {
                get => _alpha;
                set => _alpha = value;
        }
        private Alpha _alpha
    
        public virtual void PrintSelf()
        {
            alpha.printSelf();
        }
    }
    
    public class B: A
    {
        public override Alpha alpha
        {
                get => _beta;
                set{}
        }
        // public?
        private Beta _beta;
    }
    
    
    public class Main
    {
        var b = new B();
        b.PrintSelf();
    }
        
    

    请问这样设计合理吗? 另外, 为了访问β实例中的非继承字段, B 类中的β字段必须设为 public, 这样会带来额外的问题吗?

    还是说这种需求就是不合理? 已经有点绕晕了现在. 这种"嵌套的继承关系"有没有专业点的称呼?

    第 1 条附言  ·  2023-12-15 16:35:54 +08:00
    
    public class Alpha
    {
        public virtual void printSelf()
        {
        	print("this is alpha");
        }
    }
    
    public class Beta: Alpha
    {
        public override void printSelf()
        {
        	print("this is beta");
        }
    }
    
    public interface IPrint
    {
    	void PrintSelf();
    }
    
    public class A: IPrint
    {
        public virtual Alpha alpha
        {
                get => _alpha;
                set => _alpha = value;
        }
        private Alpha _alpha
    
        public void PrintSelf()
        {
            alpha.printSelf();
        }
    }
    
    public class B: A
    {
        public override Alpha alpha
        {
                get => _beta;
                set{}
        }
        // public?
        private Beta _beta;
    }
    
    
    public class Main
    {
    	private IPrint test;
    
    	public void Test()
    	{
    		test = new B();
    		test.PrintSelf();
    	}
    }
    
    第 2 条附言  ·  2023-12-16 00:07:53 +08:00

    v3

    public interface IContent
    {
        string Content {get; set;}
    }
    
    public class Alpha: IContent
    {
        public string Content {get; set;} = "this is alpha";
    }
    
    public class Beta: Alpha
    {
        public string Content {get; set;} = "this is beta";
    }
    
    public interface IPrint
    {
    	void PrintSelf();
    }
    
    public class A: IPrint
    {
        public A()
        {
            content = new Alpha();
        }
        
        public IContent content;
    
        public void PrintSelf()
        {
            print(content.Content);
        }
    }
    
    public class B: A
    {
        public B()
        {
            content = new Beta();
        }
    }
    
    public class Main
    {
    	private IPrint test;
    
    	public void Test()
    	{
    		test = new B();
            // 期望输出: this is beta
    		test.PrintSelf();
    	}
    }
    
    第 3 条附言  ·  2023-12-17 00:00:55 +08:00

    v4

    // 数据类
    public interface IText
    {
    	string Text {get; set;}
    }
    
    public class Alpha: IText
    {
    	public string Text {get; set;} = "this is alpha";
    }
    
    public class Beta: Alpha
    {
    	public string Text {get; set;} = "this is beta";
    }
    
    // 使用类
    public interface IPrint
    {
    	void PrintSelf();
    }
    
    public interface IContent
    {
    	IText Content {get; set;}
    }
    
    public class A: IPrint, IContent
    {
    	public A()
    	{
    		Content = new Alpha();
    	}
        
    	public IText Content {get; set;}
    
    	public void PrintSelf()
    	{
    		print(Content.Text);
    	}
    }
    
    public class B: A
    {
    	public B()
    	{
    		Content = new Beta();
    	}
    }
    
    // 调用
    public class Main
    {
    	private IPrint test;
    
    	public void Test()
    	{
    		test = new B();
    		// 期望输出: this is beta
    		test.PrintSelf();
    	}
    }
    
    cxe2v
        1
    cxe2v  
       2023-12-14 16:37:00 +08:00
    printSelf 方法传入一个实例参数,就可以 B 使用β实例,A 使用α实例
    GodOfParentheses
        2
    GodOfParentheses  
    OP
       2023-12-14 17:39:37 +08:00
    @cxe2v 这样的话得靠调用者手动传入α或β实例? 但是调用者往往连 a 实例和 b 实例都不区分, 而α/β又是和 A/B 高度绑定的.
    cxe2v
        3
    cxe2v  
       2023-12-15 11:29:59 +08:00
    @GodOfParentheses 调用者为啥不区分这个实例呢,不区分的话,A 跟 B 内部都用β类型就行了
    GodOfParentheses
        4
    GodOfParentheses  
    OP
       2023-12-15 16:36:03 +08:00
    @cxe2v 不好意思, 例子写的不对, 请看新的代码. A/B 类存在接口继承, 调用者持有接口实例, 所以不区分 A/B.
    我好像有点明白了, 如果把α也放到接口里是不是就等价于现在的效果?
    cxe2v
        5
    cxe2v  
       2023-12-15 17:23:54 +08:00
    接口继承的话,A,B 互不影响,实现 PrintSelf 是 AB 各自在内部实现的,你想怎么写怎么写都可以
    GodOfParentheses
        6
    GodOfParentheses  
    OP
       2023-12-16 00:13:22 +08:00
    根据新理解重新写了第三个版本. 需求的核心是: 父子类中字段 X 有着不同的实现, 希望子实例调用继承自父类的方法 Y 时(Y 方法引用 X 字段), 使用 X 字段自己的实现而非父类的实现.
    刚开始纠结的字段 X 在父子类中的继承关系其实不重要.
    C#的多态机制可以实现这点, 问题是怎么写更好, 不知道 v3 这种写法还能改进不.
    GodOfParentheses
        7
    GodOfParentheses  
    OP
       2023-12-17 00:04:45 +08:00
    v5. 我悟了, 这其实就是个 Liskov 和 Open/Close 原则的应用, "继承抽象而不是继承具体". 又是被自己菜哭的一天.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1034 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 22:28 · PVG 06:28 · LAX 14:28 · JFK 17:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.