由于实际业务描述起来更麻烦点,所以我就举个栗子吧。
我有一个机器人类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 里添加项目引用的时候提示出错了)
所以来问问大佬们,怎么实现根据不同需求,创建不同类型的实例,或者说,还能改成什么样的设计?
Robot 应该只是持有 ITool 这个接口,然后包含 DoClean()方法,方法里使用 ITool 的具体实例
是的,目前的设计就是这么个情况。
现在出现了 SuperCleanTool,但它显然不会继承你定义的 ITool 接口
是的,因为接口是先定义好的,后来出现的SuperCleanTool
,但是它的实际情况有点超出了预期,为了能把它套用到先前的设计,感觉就好像是插不上的接口要硬插。策略模式和工厂模式倒是听说过,看来我是需要去仔细研究研究了哈哈。
@jtwor 感谢老哥亲自敲代码回复,你说的“因为有点不理解为什么 Robot 继承 ITool ”, Robot
并没有继承ITool
啦。
是Robot
和ITool
放在了一起(一个命名空间?下?)啦。就比方说叫——“家政机器人军团” RobotArmy
。所以说考虑到机器人打扫房间要用到工具,就定义了工具接口。
看了您定义的 ITool
接口,里面有string UsedTool()
这个方法,但是我现在面临的问题就是,先前定义接口里没有定义这个方法,为的就是不想每次继承都实现一遍这个方法。然后看了你的回复也让我理了理思路。
可能笤帚不是个很好的例子,我来给它换成_多功能吸尘器_的Tool
吧。
这样的话,我的ITool
里就有: 开机、吸尘、洒水、拖地
然后我的Robot.DoClean()
方法里就可以顺序地写上 开机,吸尘,洒水,拖地,然后大功告成。
但是我在实现SuperCleanTool : ITool
的时候,发现这是个“带水箱自动拖地吸尘器”,它没办法真正地实现“洒水”这个方法,所以我相当于是在实现洒水的时候,写了个空的。而在最后一步“拖地”的时候, 进行了“洒水”+“拖地”。
所以就带来了问题,这个SuperCleanTool
单独用的话,是能用的,但是交给我的机器人用,他就不会了,因为他在使用这个工具的时候,洒水,水并没有被洒出来,他就不知道水撒了多了还是少了,进而不知道下一步拖地该怎么去做了(是拖一下就给水擦干了,还是拖三下才能给水擦干?因为他不知道水洒出来多少)
1
geelaw 2021-03-11 17:43:09 +08:00 via iPhone
尝试改变例子可能会起到反作用。
如果工具知道自己应该被谁使用,那么 ITool 可以有一个 CreateToolUser 方法。 |
2
hahastudio 2021-03-11 17:47:59 +08:00
> 如果给我一把这个新型笤帚,我就 new RobotPro();让它去扫地。
在我看来,问题出在这里,你需要 ITool 加一个接口让调用它的地方知道它的型号 比如 ITool 里添加一个接口,叫 GetModel(),返回型号;或者叫 GetWeight(),返回重量 这样的话,也不太需要 RobotPro |
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();//太重了挥不动 /* */ |
4
jtwor 2021-03-11 18:35:07 +08:00
其实感觉就是在问依赖反转的问题
|
5
cxe2v 2021-03-11 18:45:14 +08:00
你这个架构就有点问题
Robot 应该只是持有 ITool 这个接口,然后包含 DoClean()方法,方法里使用 ITool 的具体实例, 现在出现了 SuperCleanTool,但它显然不会继承你定义的 ITool 接口,所以这里你无法用持有的 ITool 这个接口去接住 SuperCleanTool,你只能新定义一个属性去接收,然后定义一个新的 DoCleanWithSuperCleanTool 方法去使用这个 SuperCleanTool,再然后,你只能在 Robot 外部去判定该给 Robot 的那个 Tool 属性赋值了 这个需求的话,简单的你可以参考策略模式,复杂的,你可以参考工厂模式 |
7
forgottencoast 2021-03-11 23:57:38 +08:00
@Vveeb 我建议你写一个简化的项目放在 GitHub 上,不然这样很难说清楚。
有了代码,大家一看就明白了。 |