V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Vveeb
V2EX  ›  问与答

[.Net] 新手求问一个关于 类、接口设计,实例化的问题

  •  
  •   Vveeb · 2021-03-11 17:37:18 +08:00 · 1007 次点击
    这是一个创建于 1113 天前的主题,其中的信息可能已经有所发展或是发生改变。

    由于实际业务描述起来更麻烦点,所以我就举个栗子吧。

    我有一个机器人类Robot 然后这个机器人可以扫地DoClean(),所以要给它一把笤帚UsedTool.

    然后我就定义了一个笤帚ITool的接口,只要继承并实现了接口里方法的类,就可以给Robot用来扫地,到此为止,应该是可以应对大多数情况了,应该算是一个比较正常的逻辑了,是吧?

    然后我就遇到了一个问题,出现了一个新型笤帚SuperCleanTool,它虽然勉强实现了ITool接口,但是现在的Robot.DoClean() 是达不到效果的(可能是笤帚太重了挥不动?这样的意外情况)。

    所以我就想,是不是要搞一个Robot的子类——RobotPro : Robot,在这个子类里重写一下DoClean()方法,这样的话,如果给我一把这个新型笤帚,我就new RobotPro();让它去扫地。

    但是!我该怎么让调用 /实例化 Robot的类知道什么时候该实例化出一个Robot什么时候该实例化出一个RobotPro呢?

    本来是想先判断一下ITool 对象是不是SuperCleanTool的:

    if(UsedTool is SuperCleanTool tool)
    {
        new RobotPro()
        {
            UsedTool = tool;
        }
    }
    

    但是显然是不行的,因为我的Robot & ITool都是在一个项目里的,而SuperCleanTool 是在另外的项目里,所以SuperCleanTool要实现ITool接口就要先添加Robot所在项目的引用,要在Robot的代码里这样判断,是没办法回过头去引用SuperCleanTool项目的(反正我是在 VS 里添加项目引用的时候提示出错了)

    所以来问问大佬们,怎么实现根据不同需求,创建不同类型的实例,或者说,还能改成什么样的设计?

    第 1 条附言  ·  2021-03-11 20:24:36 +08:00

    @cxe2v

    Robot 应该只是持有 ITool 这个接口,然后包含 DoClean()方法,方法里使用 ITool 的具体实例

    是的,目前的设计就是这么个情况。

    现在出现了 SuperCleanTool,但它显然不会继承你定义的 ITool 接口

    是的,因为接口是先定义好的,后来出现的SuperCleanTool,但是它的实际情况有点超出了预期,为了能把它套用到先前的设计,感觉就好像是插不上的接口要硬插。策略模式和工厂模式倒是听说过,看来我是需要去仔细研究研究了哈哈。

    @jtwor 感谢老哥亲自敲代码回复,你说的“因为有点不理解为什么 Robot 继承 ITool ”, Robot并没有继承ITool啦。 是RobotITool放在了一起(一个命名空间?下?)啦。就比方说叫——“家政机器人军团” RobotArmy。所以说考虑到机器人打扫房间要用到工具,就定义了工具接口。

    看了您定义的 ITool接口,里面有string UsedTool() 这个方法,但是我现在面临的问题就是,先前定义接口里没有定义这个方法,为的就是不想每次继承都实现一遍这个方法。然后看了你的回复也让我理了理思路。

    可能笤帚不是个很好的例子,我来给它换成_多功能吸尘器_的Tool吧。

    这样的话,我的ITool里就有: 开机、吸尘、洒水、拖地

    然后我的Robot.DoClean()方法里就可以顺序地写上 开机,吸尘,洒水,拖地,然后大功告成。

    但是我在实现SuperCleanTool : ITool 的时候,发现这是个“带水箱自动拖地吸尘器”,它没办法真正地实现“洒水”这个方法,所以我相当于是在实现洒水的时候,写了个空的。而在最后一步“拖地”的时候, 进行了“洒水”+“拖地”。

    所以就带来了问题,这个SuperCleanTool单独用的话,是能用的,但是交给我的机器人用,他就不会了,因为他在使用这个工具的时候,洒水,水并没有被洒出来,他就不知道水撒了多了还是少了,进而不知道下一步拖地该怎么去做了(是拖一下就给水擦干了,还是拖三下才能给水擦干?因为他不知道水洒出来多少)

    7 条回复    2021-03-11 23:57:38 +08:00
    geelaw
        1
    geelaw  
       2021-03-11 17:43:09 +08:00 via iPhone
    尝试改变例子可能会起到反作用。

    如果工具知道自己应该被谁使用,那么 ITool 可以有一个 CreateToolUser 方法。
    hahastudio
        2
    hahastudio  
       2021-03-11 17:47:59 +08:00
    > 如果给我一把这个新型笤帚,我就 new RobotPro();让它去扫地。
    在我看来,问题出在这里,你需要 ITool 加一个接口让调用它的地方知道它的型号

    比如 ITool 里添加一个接口,叫 GetModel(),返回型号;或者叫 GetWeight(),返回重量
    这样的话,也不太需要 RobotPro
    jtwor
        3
    jtwor  
       2021-03-11 18:33:34 +08:00
    有点乱。。。是笤帚 决定 Robot 是 pro 或者 normal 还是 Robot 的类型决定使用 pro 或者 normal 的笤帚

    下面是根据传入的笤帚 返回不同的 clean 效果
    有些 interface 的改动。。因为有点不理解为什么 Robot 继承 ITool 机器是机器 工具是工具
    /* */
    public class Robot : IRobot {
    private ITool _tool { get; set; }

    public Robot(ITool tool) {
    _tool = tool;
    }

    public void DoClean() {
    Console.WriteLine(_tool.UsedTool());
    }

    }

    public class SuperTool:ITool {
    public string _status = "太重了挥不动";
    public string UsedTool() { return _status; }
    }

    public class NormalTool:ITool {
    public string _status = "开始扫地";
    public string UsedTool() { return _status; }
    }

    public interface ITool {
    string UsedTool();
    }

    public interface IRobot {
    void DoClean();
    }



    ITool super = new SuperTool();
    ITool normal = new NormalTool();

    Robot robot = new Robot(normal);//装备普通工具
    robot.DoClean();//开始扫地
    robot = new Robot(super);//装备超级工具
    robot.DoClean();//太重了挥不动

    /* */
    jtwor
        4
    jtwor  
       2021-03-11 18:35:07 +08:00
    其实感觉就是在问依赖反转的问题
    cxe2v
        5
    cxe2v  
       2021-03-11 18:45:14 +08:00
    你这个架构就有点问题

    Robot 应该只是持有 ITool 这个接口,然后包含 DoClean()方法,方法里使用 ITool 的具体实例,
    现在出现了 SuperCleanTool,但它显然不会继承你定义的 ITool 接口,所以这里你无法用持有的 ITool 这个接口去接住 SuperCleanTool,你只能新定义一个属性去接收,然后定义一个新的 DoCleanWithSuperCleanTool 方法去使用这个 SuperCleanTool,再然后,你只能在 Robot 外部去判定该给 Robot 的那个 Tool 属性赋值了

    这个需求的话,简单的你可以参考策略模式,复杂的,你可以参考工厂模式
    Vveeb
        6
    Vveeb  
    OP
       2021-03-11 20:25:36 +08:00
    @jtwor 接了条帖子好像没有 艾特上你
    forgottencoast
        7
    forgottencoast  
       2021-03-11 23:57:38 +08:00
    @Vveeb 我建议你写一个简化的项目放在 GitHub 上,不然这样很难说清楚。
    有了代码,大家一看就明白了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   987 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 20:16 · PVG 04:16 · LAX 13:16 · JFK 16:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.