上一篇以完成任务式的方式实现了插排的功能.
其中插头的规范部分值得思考, 上文采用了 abstract class 的方式,
既然是定义规范, 为什么不用接口方式呢? 一. 下面把上面的例子改造一下, 将原来的 abstract class 改为接口.
public interface IGBElectricalAppliance
{void Input(int left, int right);
}
public class OutPut
{
public OutPut()
{
this.EACollection = new List<IGBElectricalAppliance>();
}
private List<IGBElectricalAppliance> EACollection;
public void powered(int left,int right)
{
foreach (var item in EACollection)
{
item.Input(left,right);
}
}
public void AddInput(IGBElectricalAppliance item)
{
EACollection.Add(item);
}
public void RemoveInput(IGBElectricalAppliance item)
{
EACollection.Remove(item);
}
}
public class TV : IGBElectricalAppliance
{
public void Input(int left, int right)
{
Show();
Sound();
}
private void Show()
{
Console.WriteLine("I am showing");
}
private void Sound()
{
Console.WriteLine("I am sounding");
}
}
public class ElectricKettle : IGBElectricalAppliance
{
public void Input(int left, int right)
{
Heat();
}
private void Heat()
{
Console.WriteLine("I am heating");
}
}
运行一下结果也是一样, 看起来也没啥不同的.
那么到底该用那种方法呢? 二. 现在看一下 abstract class 和 interface 的区别
二者都可以定义一些 "规范", 都不可以实例化,
但 abstract class 中可以有实现的方法, 接口不可以
假如电器有一一些共用的方法例如功率计算 (PowerCalculation) 等, 可以在 abstract class 中实现, 非常方便, 这是 interface 无法实现的, 如下面代码所示
public abstract class GBElectricalAppliance
{
public abstract void Input(int left, int right);
/// <summary>
/// 计算实时功率
/// </summary>
/// <param name="u"> 电压 </param>
/// <param name="i"> 电流 </param>
public void PowerCalculation(int u ,int i)
{
Console.WriteLine("Power:" + (u * i).ToString());
}
// 其他通用方法
}
从另一个不同来看, interface 允许继承多个, 而 abstract class 不可以.
所以我们也可以这样想, abstract class 的含义是 "是 XX", 反映到例子总就是插座要求插上的设备是国标电器.
如果我们把接口再改一下,
public interface IGBElectricalable
{
void Input(int left, int right);
}
public class Other : IGBElectricalable
{
public void Input(int left, int right)
{
doSomeThing();
}
private void doSomeThing()
{
Console.WriteLine("I am other");
}
}
只是改了个名字, 大概意思是拥有符合国标标准插头的, 注意这里不再强调是电器了.
例如某些大型机械 (上面代码中的 Other), 用电部分可能只是辅助, 再定义为电器已经有点不合适了, 它也不需要继承 GBElectricalAppliance.
三. 原来的代码调整如下
public class OutPut
{
public OutPut()
{
this.EACollection = new List<IGBElectricalable>();
}
private List<IGBElectricalable> EACollection;
public void powered(int left,int right)
{
foreach (var item in EACollection)
{
item.Input(left,right);
}
}
public void AddInput(IGBElectricalable item)
{
EACollection.Add(item);
}
public void RemoveInput(IGBElectricalable item)
{
EACollection.Remove(item);
}
}
public abstract class GBElectricalAppliance:IGBElectricalable
{
public abstract void Input(int left, int right);
/// <summary>
/// 计算实时功率
/// </summary>
/// <param name="u"> 电压 </param>
/// <param name="i"> 电流 </param>
public void PowerCalculation(int u, int i)
{
Console.WriteLine("Power:" + (u * i).ToString());
}
// 其他通用方法
}
public class TV : GBElectricalAppliance
{
public override void Input(int left, int right)
{
Show();
Sound();
}
private void Show()
{
Console.WriteLine("I am showing");
}
private void Sound()
{
Console.WriteLine("I am sounding");
}
}
public class ElectricKettle : GBElectricalAppliance
{
public override void Input(int left, int right)
{
Heat();
}
private void Heat()
{
Console.WriteLine("I am heating");
}
}
原来的 TV 和 Kettle 依然可以继承自 GBElectricalAppliance, 这样它们的共用方法 PowerCalculation 依然生效,
而 GBElectricalAppliance 继承了接口 IGBElectricalable
测试一下将 Other 也插入插排
class Program
{
static void Main(string[] args)
{
OutPut op = new OutPut();
op.AddInput(new TV());
op.AddInput(new ElectricKettle());
op.AddInput(new Other());
op.powered(220, 0);
}
}
可以看到结果中出现了 "I am other".
四: 小结
本次用接口的方式对原例子进行了改造, 进一步将插排和插入设备解耦.
文一中, 插排要求插入的设备是符合国标的电器.
本文中, 插排要求插入的设备有符合国标的插头即可, 无论什么样的设备, 无论其是否是电器.
五. 思考
由此, 现在进一步想一想, 既然是有符合国标的插头即可. 而插头无非就是 Input 一个方法.
而前两种方式无论是抽象类还是接口, 都是将设备本身放入了插排的集合中,
即 AddInput(IGBElectricalable item), 然后再由插排调用集合中设备的 Input 方法.
这貌似搞得有点复杂, 耦合度还是高, 而且也不符合实际中直插入插头而不是整个设备的事实.
那么我们是否可以将此处的参数用由插入设备本身 (观察者) 改为设备的 Input 方法呢
即 AddInput(Input _input), 然后再由插排直接调用集合中的 Input 方法.
来源: https://www.cnblogs.com/FlyLolo/p/8386191.html