【温故】设计原则:接口隔离原则

温故而知新 再看设计模式 定义:Define an object that encapsulates how a set of objects interact.Mediator。定义:Provide a way to access the elements of an aggregate object sequentially。定义:Provide a unified interface to a set of interfaces in a subsystem.Facade。 阅读详情

前言

最近在读《敏捷软件开发 原则、模式与实践》,里面提到了几大设计原则,于是想复习下,并且用C#把文中代码重新写一遍

接口隔离原则(ISP)

概念

不应该强迫客户端依赖它们不用的方法

一个简单的例子

想象有一个安全系统。在这个系统中,有一些Door对象,可以被加锁和解锁,并且Door对象知道自已是开着还是关着。

public class Door
{
    /// <summary>锁门</summary>
    public void Lock(){}
    /// <summary>解锁</summary>
    public void Unlock(){}  
    /// <summary>门是否开着</summary>
    public bool IsDoorOpen(){}
}

现在,想象一个这样的实现,TimedDoor,如果门开着的时间过长,它就会发出警报声。为了做到这点,TimedDoor对象需要和另一个名为Timer的对象交互

/// <summary>
/// 定时器类 - 管理超时通知
/// </summary>
public class Timer
{
    /// <summary>
    /// 注册超时通知
    /// </summary>
    /// <param name="timeout">超时时间(毫秒)</param>
    /// <param name="client">接收通知的客户端</param>
    public void Register(int timeout, ITimerClient client){}
}

/// <summary>
/// 定时器客户端接口 - 需要超时通知的对象实现此接口
/// </summary>
public interface ITimerClient
{
    /// <summary>超时回调方法</summary>
    void TimeOut();
}

那么TimedDoor怎么实现呢,如果你想的是下面这种,那么就违反了接口隔离原则和里氏替换原则。(里氏替换原则详见我之前的文章)

public class Door : ITimerClient
{
 	// 其他方法
	public void TimeOut() { /* 做些什么 */ }
}

public class TimedDoor : Door
{
 	// 其他方法
	public void TimeOut() { /* 做些什么*/ }
}

/// <summary>
/// 普通门 - 不需要定时功能
/// 如果Door继承了ITimerClient,这里就必须实现TimeOut(退化实现)
/// </summary>
public class RegularDoor : Door
{
 	// 其他方法
  
  // 被迫实现的退化方法 - 这就是接口污染!
  public void TimeOut() { /* 什么都不做 */ }
}
解决方案一、使用多重继承分离接口

简单明了,大多数情况都应该这样设计

public class TimedDoor : Door, ITimerClient
{
 	// 其他方法
	public void TimeOut() { /* 做些什么*/ }
}

public class Door 
{
	// 原封不动
}
解决方案二、使用委托(适配器模式)分离接口

这种方式比较绕,如果实在无法用方案一的情况下,再考虑。

/// <summary>
/// 定时门接口 - 定义定时门特有的行为
/// </summary>
public interface ITimedDoor
{
    /// <summary>门超时处理方法</summary>
    void DoorTimeOut();
}

/// <summary>
/// 定时门 - 继承Door,实现ITimedDoor(不是ITimerClient)
/// </summary>
public class TimedDoor : Door, ITimedDoor
{
  private DoorTimerAdapter _adapter;
  
  public TimedDoorWithAdapter(TimerV2 timer)
  {
    _timer = timer;
    _adapter = new DoorTimerAdapter(this); // 创建适配器
  }
  
  public override void Unlock()
  {
    	// 其他代码
		// 通过适配器注册,TimedDoor本身不直接实现ITimerClientV2
		_timer.Register(5000, _adapter);
  }
}

/// <summary>
/// 门定时器适配器 - 承担ITimerClientV2的角色
/// 将Timer的调用转发给TimedDoor
/// </summary>
public class DoorTimerAdapter : ITimerClient
{
    private readonly ITimedDoor _timedDoor;

    public DoorTimerAdapter(ITimedDoor timedDoor)
    {
        _timedDoor = timedDoor;
    }

    // ITimerClientV2实现 - 将调用委托给真正的TimedDoor
    public void TimeOut()
    {
        _timedDoor.DoorTimeOut();
    }
}

ATM用户界面的例子

想象一个ATM的系统,ATM需要支持各种不同的交易,不同交易需要处理不同的UI。现在有个Transaction基类,我们至少需要存款,取款,转账操作。还有一个UI类,有以下方法。

void RequestDepositAmount();     // 请求存款UI,只有DepositTransaction使用
void RequestWithdrawalAmount();   // 请求取款UI,只有WithdrawalTransaction使用
void InformInsufficientFunds();   // 查询余额,只有WithdrawalTransaction使用
void RequestTransferAmount();    // 请求转账UI,只有TransferTransaction使用

如果这些方法都在同一个接口中,就违反了ISP。现在要设计存款,取款,转账类,如何设计能够避免呢。

遵循ISP的设计

直接看原文的设计

/// 存款UI接口 - 只包含存款相关操作
public interface IDepositUI
{
    void RequestDepositAmount();
}
/// 取款UI接口
public interface IWithdrawalUI
{
    void RequestWithdrawalAmount();
    void InformInsufficientFunds();
}
/// 转账UI接口
public interface ITransferUI
{
    void RequestTransferAmount();
}

/// 存款交易 - 只依赖IDepositUI
public class DepositTransaction : Transaction
{
    private readonly IDepositUI _depositUI;

    public DepositTransaction(IDepositUI depositUI)
    {
        _depositUI = depositUI;
    }

    public override void Execute()
    {
        Console.WriteLine("开始存款交易...");
        _depositUI.RequestDepositAmount();
        // 处理存款...
    }
}
/// 取款交易 - 只依赖IWithdrawalUI
public class WithdrawalTransaction : Transaction
{
    private readonly IWithdrawalUI _withdrawalUI;

    public WithdrawalTransaction(IWithdrawalUI withdrawalUI)
    {
        _withdrawalUI = withdrawalUI;
    }

    public override void Execute()
    {
        Console.WriteLine("开始取款交易...");
        _withdrawalUI.RequestWithdrawalAmount();
        // 检查余额...
        _withdrawalUI.InformInsufficientFunds();
    }
}
/// 转账交易 - 只依赖ITransferUI
public class TransferTransaction : Transaction
{
    private readonly ITransferUI _transferUI;

    public TransferTransaction(ITransferUI transferUI)
    {
        _transferUI = transferUI;
    }

    public override void Execute()
    {
        Console.WriteLine("开始转账交易...");
        _transferUI.RequestTransferAmount();
    }
}

/// <summary>
/// 完整的UI类 - 实现所有分离的UI接口
/// 只有这个类和main函数需要知道所有接口的存在
/// </summary>
public class CompleteUI : IDepositUI, IWithdrawalUI, ITransferUI
{
    public void RequestDepositAmount()
    {
        Console.WriteLine("UI: 请输入存款金额");
    }

    public void RequestWithdrawalAmount()
    {
        Console.WriteLine("UI: 请输入取款金额");
    }

    public void InformInsufficientFunds()
    {
        Console.WriteLine("UI: 余额不足!");
    }

    public void RequestTransferAmount()
    {
        Console.WriteLine("UI: 请输入转账金额");
    }
}
多参数形式 vs 单参数形式

外部使用的时候更应该用多参数模式。

即使两个参数是同一个对象,也明确声明函数依赖哪些接口。

/// <summary>
/// 多参数形式 - 推荐做法
/// 函数明确声明它依赖哪些接口
/// </summary>
public class MultiParameterExample
{
    // 明确声明需要DepositUI和TransferUI
    public void ProcessTransaction(IDepositUI depositUI, ITransferUI transferUI)
    {
        depositUI.RequestDepositAmount();
        transferUI.RequestTransferAmount();
    }
}

/// <summary>
/// 单参数形式 - 不推荐
/// 强迫函数依赖于UI的所有部分
/// </summary>
public class SingleParameterExample
{
    // 虽然只需要Deposit和Transfer功能,但依赖于整个UI
    public void ProcessTransaction(CompleteUI ui)
    {
        ui.RequestDepositAmount();
        ui.RequestTransferAmount();
    }
}

// 使用对比
public class ParameterDemo
{
    public void Demo()
    {
        var ui = new CompleteUI();
        
        // 多参数形式 - 清晰,解耦
        var multi = new MultiParameterExample();
        multi.ProcessTransaction(ui, ui); // 传入同一个对象的不同接口视图
        
        // 单参数形式 - 隐含依赖整个UI接口
        var single = new SingleParameterExample();
        single.ProcessTransaction(ui);
    }
}

参考文献

敏捷软件开发 原则、模式与实践

深入探讨 VQ-VAE:理解变分量化自动编码器 VQ-VAE代表"Variational Quantized Variational Autoencoder",是一种生成模型,通常用于处理高维数据,如图像和音频。它的设计灵感来自于变分自动编码器(VAE)和量化方法,结合了它们的优点,使得 VQ-VAE 能够生成高质量的数据,并在数据表示上引入离散性。VQ-VAE 是一种强大的生成模型,结合了变分自动编码器和量化方法的优点。它在图像和音频生成、数据压缩和生成性对抗网络等领域具有广泛的应用。 阅读详情

相关推荐

IFIX往Mysql数据库同步数据方法

通过IFix数据库向Mysql数据库同步数据

仙人掌 1595

C++设计模式的设计原则(面向对象八大设计原则)

C++设计模式八大原则的讲解,面向对象八大设计原则的讲解,以简单易懂、有趣论说来讲解八大设计原则

不负AC不负卿的博客 4680

FreeCAD MCP实战:用AI自然语言驱动CAD建模与自动化

在计算机辅助设计(CAD)领域,参数化建模和自动化脚本是提升设计效率的核心技术。其原理在于通过程序化指令控制三维模型的创建与修改,从而将设计意图转化为精确的几何实体。这种技术价值在于显著降低重复性操作成本,实现快速原型迭代和设计探索。在应用场景上,它尤其适用于需要频繁调整尺寸、生成系列化零件或执行规则化建模任务的工程与产品设计。本文聚焦的FreeCAD MCP项目,正是这一理念的先进实践。它通过集成Model Context Protocol(MCP),构建了一个连接Claude AI与FreeCAD的智能

weixin_26913055的博客 723

SOLID 设计原则

温故而知新 一, 单一职责原则(Single Responsibility Principle, SRP) 优点: 1) 类的复杂性降低了, 由于我们让每个类的职责单一,因此每个类职责清楚,定义明确。 2) 可读性增强了,复杂性降低了, 类更易于维护了。 3) 变更的风险降低了, 需求一直处于变化,使用SRP原则只需要修改一个接口及它的实现类,对其他类和接口没有影响。 二, 开闭原则 (Open-...

QTperfect1的博客 327

Isaac Sim 车辆动力学:从参数调优到户外仿真的挑战与实战

本文深入探讨了Isaac Sim在车辆动力学仿真中的应用,从参数调优到户外场景模拟的实战经验。详细解析了悬架系统、轮胎物理模型等核心参数的设置技巧,并分享了应对户外仿真挑战的实用方案。通过Vehicle Wizard高级使用和传感器配置,帮助开发者高效完成车辆仿真与算法开发。

weixin_29192211的博客 404

手把手教你用Proteus仿真51单片机抢答器:倒计时+蜂鸣器报警完整项目

本文详细介绍了使用Proteus软件仿真51单片机智能抢答器的完整项目流程。通过系统架构设计、核心元件选型(如74LS30用于按键中断编码),结合状态机编程,实现了带可调倒计时、数码管显示和蜂鸣器报警的八路抢答器功能。项目涵盖了从原理图绘制到代码调试的全过程,是学习嵌入式系统开发的绝佳实践。

tcp8optimizer的博客 262

【温故而知新】面向对象编程五大原则

在面向对象设计中,如何通过很小的设计改变就可以应对设计需求的变化,这是令设计者极为关注的问题。为此不少OO先驱提出了很多有关面向对象的设计原则用于指导OO的设计和开发。下面是几条与类设计相关的设计原则。 单一职责原则SRP:Single Responsibility Principle   开放封闭原则OCP:Open-Close Principle   Lis...

Z4185146的博客 159

飞塔防火墙CVE-2022-40684漏洞复现:手把手教你搭建靶场环境(附调试技巧)

本文详细指导如何搭建Fortinet飞塔防火墙CVE-2022-40684认证绕过漏洞的靶场环境,涵盖虚拟机部署、网络配置、调试模式开启及漏洞原理分析。通过手把手教程,读者可掌握漏洞复现与深度调试技巧,深入理解该认证绕过漏洞的利用与防御。

h6i7j8的博客 695

软件架构-设计原则

这几天西安一直下雨,转眼把我们带入到了秋天。工作上越来越难取得进步,给内部集团做项目,没有强有力的领导推动,事情进展很慢。只能在学习上找到突破点,否则每天8个小时,越来越迷茫。复习旧知识,温故而知新。 设计模式是在解决问题的过程中,一些良好思路的经验集成,常见的是GoF 23种设计模式。GoF 23种设计模式的一些指导设计原则: 1、“开闭”原则(OCP):一个软件实体应当对扩展开放,...

weixin_33704234的博客 108

uniapp APP端:蓝牙报错writeBLECharacteristicValue:fail property not support(uni.writeBLECharacteristicVal)

uni-app 蓝牙fail property not support、发现不了服务特征值的问题、突然发现微信蓝牙小秘密,使用uni-app编译成安卓APP后报错writeBLECharacteristicValue:fail property not support,发现不了服务特征值,uniApp APP调用蓝牙API发送指令过快导致,uniapp 蓝牙 writeBLECharacteristicValue property_not_support iOS Android BLE GATT 特征值写入

✅ 精选推荐文章 2943

玩转大语言模型——使用华为官方MindIE-Server镜像方式部署DeepSeek模型

本文将详细介绍在国产Linux系统欧拉中使用华为官方MindIE-Server镜像方式部署DeepSeek模型。一般情况下华为昇腾800I服务器可以推理DeepSeek-R1-70B,4张300I-Duo服务器可以推理DeepSeek-R1-32B。在配置中尽量贴合官方文档,包含用到的一些其他的技术细节,便于零基础入门使用。

艾醒的博客 1289

温故而知新之——设计模式(二)

软件开发中,为了提高软件系统的可维护和可复用性,增加软件的可扩展性和灵活性,开发人员尽量根据7条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。 开闭原则(Open Closed Principle)由Bertrand Meyer提出。软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension , but c...

小蒋聊技术 196

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

[温故知新] 编程原则和模式

写了这么多年代码,依旧做不好一个项目做好一个项目是人力、产品、业务、技术、运营的结合,可能还叠加一点时机的因素,就我们码农而言,工作就是搬砖,实现产品, 给业务提供支撑。“给祖传代码加 ...

dotNET跨平台 269

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

温故知新-设计模式篇

01 Mind Map 02 Question&Answer Q1: 什么是反射、它的作用是什么? A: 运行时,动态获取这个类的信息。作用是访问对象的属性、方法、构造函数。 Q2: 反射应用场景? A: JDBC驱动加载、SpringIOC、框架等。 Q3: 反射获取类的方式以及创建对象的方式? A: 3种方式,创建对象:clazz.newInstance()。 Q4: 如何禁止...

阿甘兄 804

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

浅读设计模式之禅(一)

本文是我在读《设计模式之禅》这本书时所提炼出来的一些关键点,比较笼统,是我想在以后用他来温故知识,基本不适用于普通阅读,如有看不明白的地方还望翻书至原章原节.... 一、单一职责原则(SRP):接口一定要做到单一职责,类的设计要尽量做到只有一个原因引起它的变化 二、里氏替换原则(LSP): 1、在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则

u014655027的专栏 502

Java-23种设计模式汇总+6种面向对象设计原则(SOLID)+9种职责分配原则(GRASP)(终)

Java实现的23种设计模式汇总,及面向对象原则解释(SOLID),及指责分配原则解释(GRASP),及面向对象整个实现过程总结(OOA、OOD、OOP)。

大老龙的博客 1375

C++设计模式 温故知新李建忠(BooLan、GeekBand)

设计模式是前人的智慧和经验的总结,需要我们反复地学习、在实践中体会其蕴含的设计原则和设计思想。李老师按照封装变化将23种设计模式分成组件协作、单一职责、对象创建、对象性能、接口隔离、状态变化、数据结构、行为变化和领域问题九类,为我们理解设计模式提供了新角度。 第一讲:设计模式简介 什么是设计模式 “每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。 ——Christopher Alexander 如何解决复杂性 1.分

winniwu的博客 2692

Python面向对象编程:探索代码的结构之美

当涉及Python面向对象编程时,我们探索的不仅仅是代码的结构,更是代码的结构之美。面向对象编程以其清晰的层次结构、灵活的设计模式和可维护性而闻名。本文将带您深入了解Python中面向对象编程的精髓,从类和对象的基本概念开始,逐步探索封装、继承、多态等高级特性。通过实际示例和详细解释,我们将揭示面向对象编程如何提高代码的可读性、复用性和可维护性,从而展现代码结构的优雅之美。无论您是初学者还是有经验的开发者,本文都将为您揭开Python面向对象编程的神秘面纱,让您领略代码背后的结构之美。

忆~遂愿的博客 2938

C++学习笔记DAY6

是一种通用、可视化的面向对象建模语言,提供统一的、标准化的图形化设计语言和工具集,适用于软件工程领域的分析与设计。UML 常用图表及其作用图表类型中文名作用类图描述系统中类的内部结构以及类与类之间的关系(静态结构)时序图描述对象之间动态交互关系,展现消息在时间轴上的传递顺序协作图展现对象之间的协作关系,描述消息交互和执行顺序时序图 vs 协作图:两者都描述对象间的交互,时序图强调时间顺序,协作图强调组织结构(上下文关系)。

2301_77141494的博客 192

【第21期】以实际项目作驱动,换种方式学Java

Java作为OOP(面向对象编程)的集大成者,融合了其他语言的诸多优点,让开发者体验到了函数式编程的精妙及强大之处。随着这几年开源社区的蓬勃发展,越来越多的组件、框架、方案如雨后春笋般涌...

华章IT官方博客 336

【设计模式】Java23种设计模式 - 整合

整理了前段时间学习的设计模式

qq_43843951的博客 21万+

Java温故而知新(5)设计模式详解(23种)

一、设计模式的理解 刚开始“不懂”为什么要把很简单的东西搞得那么复杂。后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精髓所在,我所理解的“简单”就是一把钥匙开一把锁的模式,目的仅仅是着眼于解决现在的问题,而设计模式的“复杂”就在于它是要构造一个“万能钥匙”,目的是提出一种对所有锁的开锁方案。在真正理解设计模式之前我一直在编写“简单”的代码.这个“简单”不是功能的简单,而...

weixin_33933118的博客 185

浅瞥JAVA设计模式

今天学了比较高深的设计模式的相关知识。我挺老实讲都有些头大,差点就下意识的跑神儿。但是为了温故,就冒犯一下JAVA写篇关于设计模式的文章。仅是知识简单记录而已,这些设计理念是需要代码来沉淀的,就算先混个脸熟吧。 JAVA 6 种设计原则 (本应配合具体例子代码,但目前我还没有这个能力) 六中设计模式分别是:单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪...

anlx27的专栏 152

代码规范七大原则_设计模式系列:七大原则(笔记一)

之前一直想整理一些关于设计模式相关的内容,客观上来说是因为工作比较忙没有时间来干这件事情,主观上来说还是因为自己懒癌晚期,工作之余根本不想动。先终于下定决心结合工作中常用的框架通过图文的方式来介绍Gof中的23个设计模式,加上“简单工厂模式”的话一共有24个模式可介绍。在介绍具体的设计模式之前,本文先介绍设计模式的基础概念以及设计模式所需要遵循的七大原则。一、设计模式的概念设计模式:是指在软件开发...

weixin_39608116的博客 1678
上一篇: 【温故】设计原则: 里氏替换原则
下一篇: 【温故】设计原则:依赖倒置原则
ZZHSourceCode
博客等级 码龄9年 8粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值