如何拓展多态下的子类

多态是对象编程的基础。其中一个原则就是要将共用特性抽取到基类。但开发中需求总是会变:如果子类需要新增方法怎么办?难道依次修改基类和其他子类吗?

当然不是。应该遵守开闭原则(OCP),使用特化子类。

例如以下三个类是标准多态结构。

csharp 复制代码
// 接口
public interface ICommunication
{
    void Send(byte[] data);
}

// TCP 实现
public class TcpClientComm : ICommunication
{
    public void Send(byte[] data) { /* TCP 实现 */ }
}

// 串口实现
public class SerialPortComm : ICommunication
{
    public void Send(byte[] data) { /* 串口实现 */ }
}

如果 SerialPortComm 需要增加新方法怎么办?

答案是直接添加。但在调用时,将其转为子类 SerialPortComm 使用,如下图:

csharp 复制代码
// 实现特有的发送字符函数
public class SerialPortComm : ICommunication
{
    public void Send(byte[] data) { /* ... */ }
    public void SendString(string data) { /* 串口特有逻辑 */ }
}

// 调用
// C# 9.0 以上的模式匹配(最推荐)
public void ProcessCommand(ICommunication comm)
{
    // 通用部分:所有设备都发字节
    comm.Send(new byte[] { 0x01, 0x02 });

    // 特殊部分:仅当该设备"具备字符串能力"时才执行
    if (comm is IStringCommunication stringable) // 这里不转具体类,转的是扩展接口
    {
        stringable.SendString("AT+RESET");
    }
}

扩展 1

怎么避免在调用时写太多if...else... ?

答案是动态调度,好处是调用方(Main函数)一行代码都不用改。

csharp 复制代码
public class CommandProcessor
{
    // 针对 TCP 的默认处理(字节流)
    private void ExecuteInternal(ICommunication comm)
        => comm.Send(new byte[] { 0x00 });

    // 针对串口的特殊处理(重载)
    private void ExecuteInternal(SerialComm comm)
        => comm.SendString("Hello");

    // 对外暴露的唯一入口,利用 dynamic 自动分流
    public void Execute(ICommunication comm)
    {
        // 核心技巧:把 comm 转为 dynamic,编译器会推迟绑定
        // 运行时自动寻找参数类型最匹配的 ExecuteInternal
        ExecuteInternal((dynamic)comm);
    }
}

var processor = new CommandProcessor();
processor.Execute(new SerialComm()); // 输出:串口发字符串
processor.Execute(new TcpComm());    // 发送 0x00

扩展 2

怎么避免修改已有的子类?

为了符合开闭原则(OCP),可以使用适配器模式。将旧子类和新方法一起包装成一个新类来使用,如下:

csharp 复制代码
// 不修改 Serial 原有的类
public class SerialStringAdapter : IStringCommunication
{
    private readonly SerialPortComm _serial;
    public SerialStringAdapter(SerialPortComm serial) { _serial = serial; }

    public void Send(byte[] data)
    {
        _serial.Send(data);
    }

    public void SendString(string data)
    {
        // 内部转成字节调用原来的 Send
        _serial.Send(Encoding.UTF8.GetBytes(data));
    }
}
相关推荐
淡海水1 小时前
13-04-面试-源码级深度追问链
数据结构·unity·面试·c#·游戏引擎·源码·il2cpp
asdzx672 小时前
如何使用 C# 提取 PPT 文本与图片
c#·ppt·提取文本
2501_930707789 小时前
使用C#代码使用条件格式为 Excel 隔行设置颜色
c#·excel
怕浪猫9 小时前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
Zane199411 小时前
函数式编程里的函数,其实不是你天天写的那个函数——三大编程范式的边界在哪
设计模式
何以解忧唯有撸码12 小时前
【开源】c#造个轮子-日程安排工具
c#·自定义控件
周杰伦fans13 小时前
CAD菜单栏和工具栏都不见了,怎么显示?
其他·c#
曹牧13 小时前
C#:HashSet<T> 去重
c#
czhc114007566313 小时前
起点、口径与因果:今天用错的七把尺子
c#
c#上位机1 天前
C#上位机项目实战——Task.Run执行带参数的方法
c#·上位机