【温故】设计原则: 开闭原则

温故而知新 再看设计模式 定义: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#把文中代码重新写一遍

开闭原则(OCP)

概念

对扩展开放,对修改封闭。

OCP的优势

开闭原则的核心价值在于:它使软件架构具有韧性。当需求变化时,我们通过写新代码而非改旧代码来适应变化,从而保护已通过测试的稳定代码,降低维护成本,提高可重用性。

原文中提到了更重要的一点:"对于应用程序中的每个部分都肆意地进行抽象也不是一个好主意。正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。"这句话和性能优化的原则类似,拒绝过早优化。当你用开闭原则写代码像呼吸一样自然时,你就知道什么可以遵循开闭原则,什么时候可以先不用在意开闭原则。

先来看需求

我们有一个需要在标准的GUI上绘制圆和正方形的应用程序。圆和正方形必须要按照特定的顺序绘制。我们将创建一个列表,列表由按照适当的顺序排列的圆和正方形组成,程序遍历该列表,依次绘制出每个圆和正方形。

违反OCP的代码

public enum ShapeType
{
    Circle,
    Square
}

public struct Shape
{
    public ShapeType ItsType;
}

public struct Circle
{
    public ShapeType ItsType;
    public double ItsRadius;
    public Point ItsCenter;
}

public struct Square
{
    public ShapeType ItsType;
    public double ItsSide;
    public Point ItsTopLeft;
}

public class ShapeDrawer
{
    // 严重违反OCP:新增形状类型时必须修改此方法
    public void DrawAllShapes(List<Shape> list)
    {
        foreach (var s in list)
        {
            switch (s.ItsType)
            {
                case ShapeType.Square:
                    DrawSquare((Square)(object)s);
                    break;
                case ShapeType.Circle:
                    DrawCircle((Circle)(object)s);
                    break;
            }
        }
    }
    
    private void DrawSquare(Square s) => Console.WriteLine($"绘制正方形,边长:{s.ItsSide}");
    private void DrawCircle(Circle c) => Console.WriteLine($"绘制圆形,半径:{c.ItsRadius}");
}
问题所在
  1. 每次新增类型时,都要增加一个enum,和switch项。

  2. 日后会导致,switch越来愈长,难以维护。

遵循OCP的代码

使用抽象和多态。通过抽象基类隔离变化。新增形状类型时,只需添加新类,无需修改现有代码。

public abstract class Shape
{
    public abstract void Draw();
}

public class Square : Shape
{
    public double Side { get; set; }
    public Point TopLeft { get; set; }
    
    public override void Draw()
    {
        Console.WriteLine($"绘制正方形:边长={Side}");
    }
}

public class Circle : Shape
{
    public double Radius { get; set; }
    public Point Center { get; set; }
    
    public override void Draw()
    {
        Console.WriteLine($"绘制圆形:半径={Radius}");
    }
}

public class ShapeDrawer
{
    // 完全符合OCP:对扩展开放,无需修改即可支持新形状
    public void DrawAllShapes(List<Shape> shapes)
    {
        foreach (var shape in shapes)
        {
            shape.Draw(); // 多态调用,无需知道具体类型
        }
    }
}

// 使用演示
// var drawer = new ShapeDrawer();
// var shapes = new List<Shape> { new Circle(), new Square(), new Triangle() }; // 可随意添加新类型
// drawer.DrawAllShapes(shapes);

当然,需求是会持续变化的

如果我们要求所有的圆必须在正方形之前绘制,那么DrawAlShapes函数会怎样呢?DrawAlIShapes函数无法对这种变化做到封闭。要实现这个需求,我们必须要修改DrawAIShapes的实现,使它首先扫描列表中所有的圆,然后再扫描所有的正方形。

如果是你,你会怎么优雅地完成这个需求呢?

终极OCP

使用“数据驱动”的方法获取封闭性。
依据原文中的解决方案,用C#实现。以下代码可直接复制运行。


var drawer = new ShapeDrawer();
var shapes = new List<Shape> {
    new Triangle(),
    new Circle(),
    new Square()
};

drawer.DrawAllShapes(shapes);

public abstract class Shape : IComparable<Shape>
{
    private static readonly string[] TypeOrderTable = new[]
    {
            "Circle",   // 索引0:最先绘制
            "Square",   // 索引1
            "Triangle"  // 索引2
        };

    public abstract void Draw();

    // 非虚方法:完全通用的排序逻辑,无需在每个派生类中重写
    public bool Precedes(Shape other)
    {
        string thisType = this.GetType().Name;
        string otherType = other.GetType().Name;

        int thisOrder = Array.IndexOf(TypeOrderTable, thisType);
        int otherOrder = Array.IndexOf(TypeOrderTable, otherType);

        // 如果都不在表中,按名称排序;如果在,按表中的顺序
        if (thisOrder == -1 && otherOrder == -1)
            return string.Compare(thisType, otherType) < 0;

        return thisOrder < otherOrder;
    }

    public int CompareTo(Shape other) => Precedes(other) ? -1 : (other.Precedes(this) ? 1 : 0);
}

// 现在添加新形状类型(如Triangle)无需修改任何现有代码!
public class Triangle : Shape
{
    public override void Draw() => Console.WriteLine("绘制Triangle");
}
public class Circle : Shape
{
    public override void Draw() => Console.WriteLine("绘制Circle");
}
public class Square : Shape
{
    public override void Draw() => Console.WriteLine("绘制Square");
}


public class ShapeDrawer
{
    public void DrawAllShapes(List<Shape> shapes)
    {
        foreach (var shape in shapes.OrderBy(s => s))
        {
            shape.Draw();
        }
    }
}

参考文献

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

啊,如何遵循“低耦合”设计原则? 1. 引言 类的设计需要遵循“高内聚、低耦合”的设计原则(或者说“高内聚、松耦合”)。在之前的文章里介绍过什么是高内聚和低耦合(设计模式(三)——面向对象设计原则) 高内聚:内聚是对软件系统中元素职责相关性和集中度的度量。如果元素具有高度相关的职责,除了这些职责内的任务,没有其它过多的工作,那么该元素就具有高内聚性;反之则成为低内聚性。 低耦合:耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据 某种程度上理解,高内聚低耦合 阅读详情

相关推荐

AI Agent Harness Engineering 创业PMF验证工具:用户满意度+留存率+业务指标监测表

在AI Agent创业的浪潮中,我们看到了无数产品如雨后春笋般涌现,但最终能够真正找到PMF并存活下来的却寥寥无几。根据CB Insights的数据,创业公司失败的首要原因(占42%)是"没有市场需求"——换句话说,就是没有找到PMF。AI Agent产品由于其特殊性(交互范式的变革、技术复杂度高、用户期望管理困难等),PMF验证变得更加具有挑战性。传统的SaaS产品PMF验证框架虽然有参考价值,但无法完全适用于AI Agent这种新型产品形态。

AI智能探索者的博客 329

【温故】设计原则: 里氏替换原则

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

ZZHSourceCode的博客 1447

Redmi K30 5G刷 LineageOS17.1

前言 买手机前就观望了Xdr论坛,看Redmi K30 5G有LineageOS17.1的Rom并且还挺热,应该还不错,就入手了。晚上就刷机了。买了小米就是为了折腾,为发烧而生。 在开始前还请先安装adb工具,因为我自己是做Android开发的,自带adb,不会安装的可以百度“Android安装adb工具”。还有下载刷机文件:百度云 密码:2a0f 刷机的系统是刚买来的低版本,没有升级过Rom,如果高版本慎刷。 还有这个Rom是5G版本的,4G版本的不要刷。 建议没有刷过机器的最好不要尝试,变砖后果自负。

u010436867的博客 1万+

SOLID 设计原则

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

QTperfect1的博客 327

Palantir的本体论建模跟数仓建模的异同

Palantir的Foundry Ontology通过构建组织的数字孪生体,将数据集和模型映射为对象类型、属性、链接和操作类型,形成完整的语义知识网络。与传统数仓建模相比,本体建模以业务问题为驱动,强调细粒度语义和实时决策能力,采用图语义技术实现灵活的映射关系。

轩哥的个人研究和成长记录 1588

BRep实战指南:从理论到文件格式的全面解析

本文全面解析BRep(边界表示法)从基础概念到文件格式的实战应用。详细介绍了BRep的数学定义、工业应用场景,以及STEP、IGES等主流文件格式的对比与使用技巧,帮助读者掌握三维建模核心技术并优化性能。

weixin_29235923的博客 407

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

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

不负AC不负卿的博客 4680

MFC工具栏增加EditBox和Button

1. 在资源视图中增加EditBox, Button控件,ID分别为ID_EDIT_SEED, ID_BTN_SEED 2. 在CMainFrame中增加成员变量 CMFCToolBarEditBoxButton *m_editSeed; CMFCToolBarButton *m_btnSeed; 3. 在CMainFrame::OnCreate中创建控件 m_editSeed = new CMFCToolBarEditBoxButton(ID_EDIT_SEED, GetCmdMgr()-&.

学习是为了探索这个世界的本质 802

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

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

weixin_30486037的博客 65

计算机视觉与深度学习 | 2024年至2025年图像匹配算法总结(原理,公式,代码,开源链接)

以上总结覆盖了当前主流和前沿技术,实际选择需结合具体场景(速度/精度/资源限制)评估。

尘世冰封的专栏 1586

Unreal Engine开发:角色动画与状态机_动画混合:通过混合节点实现平滑动画过渡

动画混合是提升游戏动画质量的关键技术之一。Unreal Engine提供了多种混合节点,包括BlendSpace1D、Blend Two By Bool、Blend Two By Alpha和Sequencer,每种节点适用于不同的场景。通过合理使用这些节点,可以实现平滑、自然且高效的动画过渡效果。此外,遵循最佳实践和高级应用技巧,可以进一步优化动画质量和性能。希望本节内容对你在Unreal Engine中实现动画混合有所帮助,使你的游戏角色动作更加流畅和自然。

chenlz2007的博客 1245

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

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

小蒋聊技术 196

老 Intel Mac mini 装 OpenClaw:低成本常年在线方案

老款Intel Mac mini部署OpenClaw方案摘要 本文详细介绍了如何利用老款Intel Mac mini低成本部署OpenClaw系统。方案推荐2011-2018款Mac mini,建议升级至16GB内存和SSD存储以提升性能。部署步骤包括:1)安装Ubuntu系统;2)配置Docker环境;3)通过docker-compose部署OpenClaw及其依赖组件(PostgreSQL和Redis);4)进行系统级和Docker层面的性能优化;5)设置开机自启动服务。该方案充分利用老款设备剩余价值,

一键难忘的博客 2476

软件架构-设计原则

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

weixin_33704234的博客 108

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

温故知新-设计模式篇

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

阿甘兄 804

jre9下载 jdk9

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

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

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

大老龙的博客 1375

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

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

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

qq_43843951的博客 21万+

Microsoft NLayerApp案例理论与实践 - 多层架构与应用系统设计原则

Microsoft NLayerApp案例理论与实践 - 多层架构与应用系统设计原则 在对NLayerApp实际项目进行讨论之前,让我们首先学习一下(或者应该说重温一下)分层/多层架构与应用系统设计原则。很多朋友会认为这些都是老掉牙的内容,只要是软件从业人员,都会对这些内容非常熟悉。然而,果真如此吗?我在这里整理这部分内容,一方面是为介绍NLayerApp打下基础,而另一方面

M醉清风Y的博客 742

C++学习笔记DAY6

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

2301_77141494的博客 192

浅读设计模式之禅(一)

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

u014655027的专栏 502

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

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

忆~遂愿的博客 2938

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

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

winniwu的博客 2692

Microsoft NLa“.NET研究”yerApp案例理论与实践 - 多层架构与应用系统设计原则

  在对NLayerApp实际项目进行讨论之前,让我们首先学习一下(或者应该说重温一下)分层/多层架构与应用系统设计原则。很多朋友会认为这些都是老掉牙的内容,只要是软件从业人员,都会对这些内容非常熟悉。然而,果真如此吗?我在这里整理这部分内容,一方面是为介绍NLayerApp打下基础,而另一方面,则是希望借此机会将这些理论性的东西做个归纳,也希望读者朋友能够认真阅读,毕竟温故知新嘛。   需要说...

weixin_33721427的博客 76

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

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

华章IT官方博客 336

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

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

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值