面向对象设计:如何基于职责原则合理放置新函数

面向对象设计:如何基于职责原则合理放置新函数

在面向对象设计中,新功能的放置并非简单的技术选择,而是对solid/grasp等设计原则及对象职责的深刻理解。本文将探讨如何根据功能所处的具体上下文和其核心职责,判断是将函数作为实例方法、静态工厂方法,还是独立的服务或用例类的方法,从而构建出更清晰、更可维护的系统。

在面向对象编程(OOP)中,当需要设计一个新功能foo,它接收类型A的实例并产生类型B的实例时(假设A和B是接口),我们常常面临两种直观的设计选择:

设计一:将foo作为A的实例方法

class A {  B foo() { /* … */ }}

设计二:将foo作为B的静态方法,接收A作为参数

class B {  static B foo(A a) { /* … */ }}

从纯技术角度来看,这两种设计在实现上可能没有本质区别。然而,OOP的精髓在于其设计原则,如SOLID原则和GRASP模式,它们指导我们如何根据对象的“职责”来做出更优的设计决策。核心在于,一个函数应该属于哪个对象,取决于它与哪个对象的关系最紧密,以及谁应该承担执行该操作的“职责”。

核心原则:职责导向设计

在OOP中,函数(或方法)的放置应遵循职责分离的原则。每个类或对象都应有明确的、单一的职责。当我们考虑将foo函数放置在A、B或一个全新的类中时,我们需要深入分析foo操作的本质以及它所涉及的对象。

常见设计模式与职责分配

以下将通过具体示例,阐述在不同场景下,如何基于职责原则选择最合适的函数放置方式。

1. 当操作是对象的核心行为时:实例方法

如果foo操作是A类型对象自身的核心行为或状态转换的一部分,那么将其作为A的实例方法是自然的选择。在这种情况下,A是执行该操作的主体。

示例:订单的放置操作

假设A是Order(订单),B是ProcessingResult(处理结果),而foo是Place(下订单)操作。下订单是订单对象自身的一个行为,它会改变订单的状态或触发与订单相关的处理流程。

public class Order {   private OrderStatus status; // 订单状态   // ... 其他订单属性   public ProcessingResult Place() {        // 执行下订单的逻辑,例如:       // 验证订单、扣除库存、生成支付请求等       if (isValid()) {           this.status = OrderStatus.PLACED;           System.out.println("订单已成功放置。");           return new ProcessingResult(true, "订单放置成功");       } else {           System.err.println("订单验证失败。");           return new ProcessingResult(false, "订单验证失败");       }   }   private boolean isValid() {       // 订单验证逻辑       return true; // 简化示例   }}public class ProcessingResult {    private boolean success;    private String message;    public ProcessingResult(boolean success, String message) {        this.success = success;        this.message = message;    }    // ... getter methods}

在这个例子中,Place方法直接作用于Order实例,并利用Order的内部状态来完成操作,因此将其作为Order的实例方法符合“内聚性”原则。

2. 当操作是对象的创建或辅助构建时:静态工厂方法

如果foo操作的目的是根据某些输入(例如A)来创建B的实例,并且A更像是构建B的参数或辅助信息,那么将foo作为B的静态工厂方法是一个合理的选择。这种模式将对象的创建逻辑封装在被创建的类中。

示例:从参数创建新对象

假设A是Parameters(参数类),B是一个领域类,而foo是Create(创建)方法。Create方法接收Parameters对象,并根据这些参数构造一个B的实例。

public class Parameters {    private String name;    private int value;    public Parameters(String name, int value) {        this.name = name;        this.value = value;    }    // ... getter methods}public class B {    private String internalName;    private int internalValue;    private B(String internalName, int internalValue) {        this.internalName = internalName;        this.internalValue = internalValue;    }    public static B Create(Parameters parameters) {        // 根据参数创建B的实例        if (parameters.getValue() > 0) {            return new B(parameters.getName() + "_processed", parameters.getValue() * 2);        } else {            throw new IllegalArgumentException("Invalid parameters for B creation.");        }    }    // ... getter methods}

在这种情况下,Create方法的职责是根据给定的参数构造并返回一个B的实例,它与B的构造紧密相关,因此作为B的静态方法是合适的。

注意事项: 当创建逻辑变得复杂,或者需要创建不同类型的B时,也可以考虑引入一个独立的工厂类(例如BFactory),将创建逻辑从B类中分离出来,以遵循单一职责原则。

3. 当操作是跨多个对象的协调或用例执行时:独立的服务/用例类

如果foo操作本身不属于A或B的核心职责,而是代表一个更高级别的业务流程、用例或服务,它可能需要协调多个对象来完成任务。在这种情况下,创建一个独立的类(例如C)来封装这个操作是最佳实践。这在分层架构(如六边形架构、DDD)中尤为常见。

示例:用例的执行

假设A是FooUseCaseParameters(用例参数),B是FooUseCaseResult(用例结果),而foo是Execute(执行)操作。这个Execute操作代表了一个独立的业务用例,它可能需要调用多个领域对象或服务来完成其逻辑。

public class FooUseCaseParameters {    private String inputData;    // ... getter methods}public class FooUseCaseResult {    private String outputData;    // ... getter methods}public class FooUseCase {    // 可能依赖于其他服务或仓储    // private SomeService someService;     public FooUseCaseResult Execute(FooUseCaseParameters parameters) {        System.out.println("执行用例:" + parameters.getInputData());        // 1. 验证参数        // 2. 调用领域服务或仓储获取/修改数据        // 3. 组合结果        String processedData = parameters.getInputData().toUpperCase() + "_PROCESSED";        return new FooUseCaseResult(processedData);    }}

在这里,FooUseCase类承担了执行特定业务用例的职责。它不依附于任何特定的数据实体A或B,而是作为一个协调者或服务提供者存在。这种设计有助于保持领域模型(A和B)的纯粹性,并将业务逻辑与数据结构分离,提高了系统的可维护性和可测试性。

总结与建议

选择新功能的放置位置,是面向对象设计中的一个关键决策,它直接影响代码的内聚性、耦合度、可读性和可维护性。没有一劳永逸的答案,但遵循以下原则可以帮助我们做出明智的选择:

职责单一原则(SRP):每个类应该只有一个改变的理由。将功能放置在最能体现其单一职责的类中。高内聚,低耦合:功能应与其所操作的数据或最相关的逻辑紧密结合(高内聚),同时减少与其他不相关模块的依赖(低耦合)。领域模型驱动:考虑功能在领域模型中的自然归属。如果它是某个领域对象的核心行为,就放在该对象中。上下文决定:功能所处的具体业务上下文和系统架构(如DDD、六边形架构)会强烈影响其最佳放置位置。可读性与意图清晰:代码应该清晰地表达其意图。方法的名称和其所在类的名称应共同构成一个易于理解的语义单元。

通过以上分析,我们可以看到,在面向对象设计中,函数放置的考量远超技术实现,而是深入到对系统职责划分和架构模式的理解。在实践中,应根据具体场景灵活运用这些原则,以构建出健壮、可扩展的软件系统。

以上就是面向对象设计:如何基于职责原则合理放置新函数的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1536427.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
FullCalendar多实例同步:实现事件更新后自动刷新列表视图
上一篇 2025年12月21日 03:16:33
面向对象设计中新功能放置的考量与实践
下一篇 2025年12月21日 03:16:46

相关推荐

  • 医院处方颜色分类解析

    医院处方根据用途的不同被划分为多种类型,常见的包括门诊处方、急诊处方以及特殊管理药品处方。为了便于识别与管理,各类处方所使用的纸张颜色各不相同,例如普通处方为白色,急诊处方为淡黄色,麻醉类药品处方则为淡红色等。 1、 普通处方使用白色印刷用纸,无特殊标识。 2、 急诊处方采用淡黄色纸张,右上角明确标…

    2026年9月26日
    100
  • Java 8中的Stream API有哪些常用操作?它是惰性求值的吗?

    Java 8中的Stream API有哪些常用操作?它是惰性求值的吗?Java 8中的Stream API有哪些常用操作?它是惰性求值的吗?Java 8中的Stream API有哪些常用操作?它是惰性求值的吗?Java 8中的Stream API有哪些常用操作?它是惰性求值的吗?

    答案:Java 8的Stream API通过中间操作和终端操作实现惰性求值,提升性能与代码可读性。中间操作如filter、map返回新流且惰性执行,终端操作如forEach、collect触发计算并产生结果。惰性求值避免不必要的计算,支持短路操作,优化管道处理,适用于无限流。使用时需避免副作用、重复…

    2026年9月26日 • 用户投稿
    100
  • 谈谈你对Spring AOP的理解,它有哪些实现方式?

    谈谈你对Spring AOP的理解,它有哪些实现方式?谈谈你对Spring AOP的理解,它有哪些实现方式?谈谈你对Spring AOP的理解,它有哪些实现方式?谈谈你对Spring AOP的理解,它有哪些实现方式?

    Spring AOP通过代理机制实现横切关注点的分离,提升代码模块化与可维护性。它基于JDK动态代理或CGLIB生成代理对象,在运行时织入增强逻辑,适用于方法拦截场景;而AspectJ支持更广泛的织入方式和连接点,适合复杂需求。两者可结合使用,Spring AOP常用且易用,AspectJ强大但复杂…

    2026年9月26日 • 用户投稿
    000
  • 苹果wapi启动与不启动的区别

    苹果wapi启动与不启动的区别苹果wapi启动与不启动的区别苹果wapi启动与不启动的区别苹果wapi启动与不启动的区别

    启动 WAPI 增强安全性,支持 WAPI 加密网络连接,而关闭 WAPI 兼容性更广,安全性稍弱。 苹果 WAPI 启动与不启动的区别 主要区别: 连接能力: 启动 WAPI:支持 WAPI 网络连接,能够访问使用 WAPI 加密技术的无线网络。不启动 WAPI:不支持 WAPI 连接,无法访问仅…

    2026年9月26日 • 用户投稿
    000
  • 如何在Java中使用protected修饰符

    protected成员可在同类、同包及其他包的子类中访问,主要用于继承;子类不能通过父类实例访问其protected成员,只能继承访问。 在Java中,protected 是一种访问修饰符,用于控制类成员(字段、方法、构造器或内部类)的可见性。它比 private 更宽松,但比 public 更严格…

    2026年9月26日
    100
  • Z790主板比B760主板强在哪里?

    Z790主板相比B760在超频支持、供电能力与扩展性上更强。1. Z790支持带“K”后缀CPU超频,B760不支持;2. Z790供电模组更豪华,散热设计更强,可应对高功耗CPU长时间满载;3. Z790拥有8条DMI通道,扩展接口更丰富,支持更多高速设备;4. Z790对高频内存支持更好,内存超…

    2026年9月26日
    100
  • 从 0 开始学 V8 漏洞利用之 V8 通用利用链(二)

    作者:hcamael@知道创宇404实验室 相关阅读:从 0 开始学 V8 漏洞利用之环境搭建(一)经过一段时间的研究,先进行一波总结,不过因为刚开始研究没多久,也许有一些局限性,以后如果发现了,再进行修正。 概述 ‍我认为,在搞漏洞利用前都得明确目标。比如打CTF做二进制的题目,大部分情况下,目标…

    2026年9月26日
    100
  • sublime怎么解决mac上无法使用命令行subl的问题_sublime Mac命令行Subl问题解决

    sublime怎么解决mac上无法使用命令行subl的问题_sublime Mac命令行Subl问题解决sublime怎么解决mac上无法使用命令行subl的问题_sublime Mac命令行Subl问题解决sublime怎么解决mac上无法使用命令行subl的问题_sublime Mac命令行Subl问题解决sublime怎么解决mac上无法使用命令行subl的问题_sublime Mac命令行Subl问题解决

    首先确认Sublime Text已安装在/Applications/Sublime Text.app,然后通过sudo ln -s /Applications/Sublime Text.app/Contents/SharedSupport/bin/subl /usr/local/bin/subl创建…

    2026年9月26日 • 用户投稿
    100
  • 多模态与单模态效果有何差异 实际应用中两种模型的优劣比较

    多模态与单模态效果有何差异 实际应用中两种模型的优劣比较多模态与单模态效果有何差异 实际应用中两种模型的优劣比较多模态与单模态效果有何差异 实际应用中两种模型的优劣比较多模态与单模态效果有何差异 实际应用中两种模型的优劣比较

    本文将围绕多模态与单模态模型的差异展开叙述,旨在阐明两者在实际应用中的具体效果与优劣。文章会首先解析两种模型的基本概念,然后通过对比它们在处理信息、应对复杂任务等方面的能力,讲解其核心区别。最后,会提供一个基于应用场景的选择思路,帮助您理解如何根据具体需求来判断哪种模型更为适用。 ☞☞☞AI 智能聊…

    2026年9月26日 • 用户投稿
    100
  • 京东商户需要提供仓库信息吗_京东商户仓库信息填写及物流要求指南

    京东商户需要提供仓库信息吗_京东商户仓库信息填写及物流要求指南京东商户需要提供仓库信息吗_京东商户仓库信息填写及物流要求指南京东商户需要提供仓库信息吗_京东商户仓库信息填写及物流要求指南京东商户需要提供仓库信息吗_京东商户仓库信息填写及物流要求指南

    京东商户需根据自营或POP模式确定仓库责任,POP商家须在后台填写真实仓库信息并选择物流方式,确保24小时内发货以满足平台时效要求。 如果您计划在京东平台开设店铺并提供商品,平台会根据您的经营模式对仓储和物流提出相应要求。不同的合作模式决定了您是否需要自行提供仓库信息以及具体的物流执行方式。以下是关…

    2026年9月26日 • 用户投稿
    100
  • Java微服务分布式事务实战:TCC模式与Seata框架深度整合

    Java微服务分布式事务实战:TCC模式与Seata框架深度整合Java微服务分布式事务实战:TCC模式与Seata框架深度整合Java微服务分布式事务实战:TCC模式与Seata框架深度整合Java微服务分布式事务实战:TCC模式与Seata框架深度整合

    TCC模式结合Seata框架是微服务中实现分布式事务的可靠方案,通过Try-Confirm-Cancel机制将事务控制提升至业务层,Seata以@GlobalTransactional和@TwoPhaseBusinessAction注解简化事务协调,实现资源的预留、确认与回滚,解决数据一致性难题。 …

    2026年9月26日 • 用户投稿
    200
  • 查询本机IP地址指南—实用教程解析本地网络IP方法

    查询本机IP地址指南—实用教程解析本地网络IP方法查询本机IP地址指南—实用教程解析本地网络IP方法查询本机IP地址指南—实用教程解析本地网络IP方法查询本机IP地址指南—实用教程解析本地网络IP方法

    先查内网IP可使用命令提示符输入ipconfig,或通过系统设置查看网络属性;查公网IP则在浏览器搜索“我的IP”或访问ip.cn等网站即可。 想知道自己的电脑IP地址在哪看?其实方法很简单,主要分清你要查的是内网IP还是公网IP。内网IP是你在家庭或公司局域网里的身份标识,而公网IP是你的网络在互…

    2026年9月26日 • 用户投稿
    400
  • 宏碁掠夺者战斧18评测 机械键盘轴体手感分析

    宏碁掠夺者战斧18评测 机械键盘轴体手感分析宏碁掠夺者战斧18评测 机械键盘轴体手感分析宏碁掠夺者战斧18评测 机械键盘轴体手感分析宏碁掠夺者战斧18评测 机械键盘轴体手感分析

    宏碁掠夺者战斧18的机械键盘手感出色,是笔记本键盘中的惊喜之作。1. 它搭载cherry mx ulp超薄机械轴,提供清晰段落感与触发反馈,按键干脆、响应迅速;2. 键程虽短但手感直接,适合游戏与快速输入,长时间使用不易疲劳;3. 声音清脆带“咔哒”声,适合喜欢听觉反馈的用户,但在安静环境可能稍显明…

    2026年9月25日 • 用户投稿
    200
  • Bukkit插件开发:正确处理物品显示名称与玩家识别

    Bukkit插件开发:正确处理物品显示名称与玩家识别Bukkit插件开发:正确处理物品显示名称与玩家识别Bukkit插件开发:正确处理物品显示名称与玩家识别Bukkit插件开发:正确处理物品显示名称与玩家识别

    本文旨在解决Bukkit插件开发中,从BlockPlaceEvent获取物品显示名称并将其用于玩家识别时常见的“乱码”问题。我们将深入探讨Component对象与纯文本字符串的区别,并提供两种核心解决方案:直接获取放置方块的玩家名称,以及如何正确地将Component转换为纯文本字符串,以避免不必要…

    2026年9月25日 • 用户投稿
    400
  • MySQL怎样设置字符集 UTF8与字符集转换全解析

    MySQL怎样设置字符集 UTF8与字符集转换全解析MySQL怎样设置字符集 UTF8与字符集转换全解析MySQL怎样设置字符集 UTF8与字符集转换全解析MySQL怎样设置字符集 UTF8与字符集转换全解析

    mysql字符集设置和转换的核心是统一使用utf8mb4以支持所有unicode字符,包括emoji。1. 服务器级别设置通过修改my.cnf或my.ini文件中的character-set-server和collation-server参数实现;2. 数据库级别在创建或修改数据库时指定charac…

    2026年9月25日 • 用户投稿
    200
  • VSCode 怎样设置编辑器的字体连写效果 VSCode 字体连写效果的创意设置教程​

    要让vscode支持字体连写,需先安装支持连写的字体如fira code,再在settings.json中配置”editor.fontfamily”并将”editor.fontligatures”设为true,最后重启vscode验证效果;若不生效,检…

    2026年9月25日
    1300
  • PHP文件引入时参数传递机制详解与最佳实践

    在php中,直接通过url查询字符串方式向`require`或`include`引入的文件传递参数是无效的,这会导致“未定义变量”错误。本文将深入探讨php文件引入的原理,并提供三种正确的参数传递方法:利用作用域共享、手动填充`$_get`数组,以及推荐的通过函数或类进行封装,旨在帮助开发者构建更健…

    2026年9月25日
    100
  • quick与quickly用法区别解析

    quick与quickly用法区别解析quick与quickly用法区别解析quick与quickly用法区别解析quick与quickly用法区别解析

    1、 null 2、 词性不同 3、 quick作为形容词,意为速度快、反应灵敏、时间短或动作快捷。 4、 quickly是副词,用来描述动作的迅速进行,也可表示事情很快发生、不久之后或即刻执行。 5、 侧重点不同 6、 quick既可作定语修饰名词,也可在句中作表语使用。 7、 quickly可用…

    2026年9月25日 • 用户投稿
    400
  • 微信小店客服怎么接入?微信小店怎么增加客服

    微信已成为我国最大的社交平台之一。微信小店作为微信电商的重要组成部分,为广大商家提供了便捷的线上销售渠道。为了提升用户体验,商家需要搭建一套高效的客服体系。本文将详细介绍微信小店客服接入方法,帮助商家实现高效服务。 一、微信小店客服接入概述 1. 客服接入方式 微信小店客服接入主要有以下两种方式: …

    2026年9月25日
    300
  • 统一解析ISO Zoned Date-Time格式的日期字符串

    统一解析ISO Zoned Date-Time格式的日期字符串统一解析ISO Zoned Date-Time格式的日期字符串统一解析ISO Zoned Date-Time格式的日期字符串统一解析ISO Zoned Date-Time格式的日期字符串

    本教程详细阐述如何在Java 8+中使用java.time API统一解析看似不同但实则遵循ISO 8601扩展ISO_ZONED_DATE_TIME格式的日期字符串。通过ZonedDateTime的直接解析能力和OffsetDateTime结合DateTimeFormatter.ISO_ZONED…

    2026年9月25日 • 用户投稿
    100

发表回复

登录后才能评论
关注微信