当前位置: 首页 > 编程日记 > 正文

小酌重构系列[8]——提取接口

前言

世间唯一“不变”的是“变化”本身,这句话同样适用于软件设计和开发。
在软件系统中,模块(类、方法)应该依赖于抽象,而不应该依赖于实现。

当需求发生“变化”时,如果模块(类、方法)依赖于具体实现,具体实现也需要修改;
如果模块(类、方法)依赖于接口,则无需修改现有实现,而是基于接口扩展新的实现。

面向实现?面向接口?

接口可以被复用,但接口的实现却不一定能被复用。

面向实现编程,意味着软件的模块(类、方法)之间的耦合性非常高,每次遭遇“变化”,都会涉及到修改,并且可能是牵一发而动全身的。
每次修改,都需要对原有的代码重新测试,也可能给旧的代码引入新的错误。

面向接口编程,是为了应对软件设计和开发中的“变化”,它是一种“以不变应万变”的思维模式。
只要确保我们的抽象(接口)是不变的,无论需求怎么变化,我们总能通过扩展新的实现自如地应对。

接口是稳定的,关闭的,但接口的实现是可变的,开放的。

开闭原则

“依赖于抽象,而不是具体实现”,它同时也是开闭原则的一种体现。

开闭原则的定义:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。它是面向对象的基本原则之一。

开闭原则主要有两个特征:
(1)扩展开放(Open for extension)

(2)修改关闭(Closed for modification)
开闭原则要求模块(类、方法)应具备良好的扩展性,同时对现有的功能具有一定的保护能力。

开闭原则是一个比较模糊的一个原则,它没有告诉你如何才能对扩展开放,以及如何才能对修改关闭。
这需要借助我们自身的经验,以及对需求的理解程度,去分析软件系统中抽象的部分,识别其中的“变化”和“不变”。

提取接口

“提取接口”是面向对象编程常用的解耦策略,将一些可能发生变化的具体实现提取为接口,将“变化”封装起来,从而达到依赖接口、而非具体实现的目的。

示例

重构前

以下是一个课程注册的场景,这段代码提供了2个类:ClassRegistration和RegistrationProcessor,RegistrationProcessor依赖于ClassRegistration的Create()方法和Total属性。

public class ClassRegistration
{public void Create(){// create registration code}public decimal Total { get; private set; }
}public class RegistrationProcessor
{public decimal ProcessRegistration(ClassRegistration registration){registration.Create();return registration.Total;}
}

假如系统的业务发生了变化,ClassRegistration类的Create()方法已经不能满足新的业务了,我们需要使用另外的注册方法。
这意味着,我们需要修改ClassRegistration类的Create()方法,且需要让其同时满足旧业务和新业务。
image

RegistrationProcessor依赖于ClassRegistration,既然ClassRegistration存在着诸多变数,我们可以使用“提取接口”的重构策略,让RegistrationProcessor依赖于某个接口。

重构后

重构后,RegistrationProcessor依赖于IClassRegistration接口,RegistrationProcessor不必去关心IClassRegistration的具体实现是什么。
新的业务要求不同的Create()方式时,我们无需更改现有的ClassRegistration,而是添加新class并实现IClassRegistration接口。

另外,在不同场景下,旧业务和新业务的实现可能在不同场景下被使用。
这时,我们可以借助IoC框架将IClassRegistration接口的实例注入到指定场景。

public interface IClassRegistration
{void Create();decimal Total { get; }
}public class ClassRegistration : IClassRegistration
{public void Create(){// create registration code}public decimal Total { get; private set; }
}public class RegistrationProcessor
{public decimal ProcessRegistration(IClassRegistration registration){registration.Create();return registration.Total;}
}

最后,下面这幅图描述了这次重构过程。

image

小酌重构系列目录汇总
关注keepfool

相关文章:

多线程并发编程需要注意虚假唤醒Spurious wakeup

虚假唤醒 Spurious wakeup 如果等待线程在没有通知被调用的情况下唤醒,则称为Spurious wakeup。 解决方案就是: 使用while条件判断,更好的方案是避免使用wait这种低级的API,而是使用高级的并发工具。 因为这些高级的并发工具都是经过无数…

高中计算机个人总结怎么写,毕业生自我总结范文

毕业生自我总结范文1时光飞逝,三年忙碌而充实的大学生活在一片有序的繁忙中将要过去了,回首过去三年,内心感慨万千。我是20xx年春参加电大机械班的学习,回顾这三年的电大学习之路,饱含了汗水和收获。总结是一面镜子&am…

python - 字符串的格式化输出

# -*- coding:utf-8 -*-project: jiaxyauthor: Jimmyfile: study_2_str.pyide: PyCharm Community Editiontime: 2018-11-01 15:12blog: https://www.cnblogs.com/gotesting/# 字符串s #空字符串# 1:字符串拼接# 1.1:字符串与字符串的拼接用 连接s_1 hellos_2 worlds_3 5…

一个冷僻的知识点try直接返回finally里的设置null其实无效

先看引用类型的代码&#xff1a; import java.util.HashMap; import java.util.Map;public class trycatchefinally {public static void main(String[] args) {System.out.println(getMap().get("KEY"));}public static Map<String, String> getMap() {Map&l…

​身份证工具类

2019独角兽企业重金招聘Python工程师标准>>> 身份证工具类 package com.pqs.common.tools;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util…

目前常用的服务器端网络操作系统有,目前常用的服务器端网络操作系统是()。...

在一台IP地址为192.168.1.1的Windows Server 2008 R2计算机上安装活动目录&#xff0c;建立一个森林根域test.com&#xff0c;保留默认的域功能级别&#xff0c;同时使该计算机成为域test.com的首台域控制器并承担着DNS服务器和全局编录服务器的角色。  操作步骤&#xff…

如何处理用代码创建SD Sales order时遇到的错误消息KI 180

2019独角兽企业重金招聘Python工程师标准>>> 错误消息KI 180:You must enter a company code for transaction Create sales document 代码: REPORT zcreate_so.DATA: ls_header TYPE bapisdhd1,ls_headerx TYPE bapisdhd1x,lt_bapiret2 LIKE bapiret2 OCCURS …

Flink 基本原理与生产实践分享【入门必读,概念清晰】

Flink 基本原理与生产实践分享【入门必读&#xff0c;概念清晰】 https://zh.wikipedia.org/zh-hans/Apache_Flink Apache Flink是由Apache软件基金会开发的开源流处理框架&#xff0c;其核心是用Java和Scala编写的分布式流数据流引擎。Flink以数据并行和流水线方式执行任意流…

数据库模型设计——主键的设计

在数据库设计时&#xff0c;主要就是对实体和关系的设计&#xff0c;实体表现出来就是表&#xff0c;关系表现出来就是外键。而对于一个表&#xff0c;由两部分组成&#xff1a;主键和属性。主键的简单定义就是表中为每一行数据的唯一标识。其实更准确的说法&#xff0c;每一行…

jsp ajax动态添加数据,jquery Ajax实现Select动态添加数据

jquery Ajax实现Select动态添加数据&#xff0c;具体内容如下1.背景最近在工作中&#xff0c;遇到了一个关于select的问题。一般情况下&#xff0c;select下拉框中的数据都是固定的或者直接在jsp中读取列表值显示。但是&#xff0c;这次要实现select与别的选项框联动&#xff0…

Spring Boot(十一)Redis集成从Docker安装到分布式Session共享

2019独角兽企业重金招聘Python工程师标准>>> 一、简介 Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API&#xff0c;Redis也是技术领域使用最为广泛的存储中间件&#xff0c;它是「…

Java线上问题排障:Linux内核bug引发JVM死锁导致线程假死

Java本质上还是离不开操作系统&#xff0c;一来Java源码是用C/C实现的&#xff0c;二来java进程还是需要依附于操作系统和硬件资源&#xff0c;有时候一些问题是操作系统级别导致的&#xff0c;下面的整个事件是源自一则真实的线上案例。 过程&#xff1a; JVM死锁导致线程不可…

从AdventureWorks学习数据库建模——保留历史数据

在业务需求中&#xff0c;经常需要我们在系统中能够记录历史信息&#xff0c;能够查看到历史变动情况&#xff0c;这时我们可以通过增加开始结束时间字段来记录数据的历史版本。对数据的历史记录主要分为&#xff1a;关系、属性历史&#xff0c;实体历史和变更历史。 关系、属性…

因特网的域名服务器系统的好处,dns域名服务器的作用是什么

大家好&#xff0c;我是智能客服时间君&#xff0c;上述问题将由我为大家进行解答。dns是域名系统 (Domain Name System) 的缩写&#xff0c;该系统用于命名组织到域层次结构中的计算机和网络服务。DNS 命名用于Internet等TCP/IP网络中&#xff0c;通过用户友好的名称查找计算机…

Openssl私建CA

构建私有CA: 在确定配置为CA的服务上生成一个自签证书&#xff0c;并为CA提供所需要的目录及文件即可&#xff1b;步骤&#xff1a; (1) 生成私钥&#xff1b;[rootcentos7 ~]# (umask 077; openssl genrsa -out /etc/pki/CA/private/cakey.pem 4096)Generating RSA priva…

不同版本浏览器前端标准兼容性对照表以及CORS解决跨域和CSRF安全问题解决方案

CORS也已经成为主流的跨域解决方案&#xff0c;不过CORF也会引发CSRF&#xff0c;本文先分享第三方的一个前端工具箱全面展示那些浏览器版本支持CORS&#xff0c;由于各家浏览器厂商因为各自原因在不同的版本里支持的标准不同&#xff0c;这个工具小而美&#xff0c;可以清晰的…

arm服务器获取文件路径中文,ssh 访问远程服务器文件路径

ssh 访问远程服务器文件路径 内容精选换一换在IntelliJ上选择“项目”&#xff0c;找到“.idea”文件夹&#xff0c;单击右键选择“新建>文件”&#xff0c; 输入文件名“settings.json”生成settings.json文件。如图1所示。复制以下代码至新创建的settings.json文件中&…

[Win7]如何还原[.bat]文件关联

2019独角兽企业重金招聘Python工程师标准>>> 此文已迁移到微信公众号&#xff1a;灰灰的Rom笔记&#xff0c;公众号ID&#xff1a;SXF-Rom。 灰灰的Rom笔记 转载于:https://my.oschina.net/shawnxia/blog/672371

Android Monkey使用

Monkey 是什么&#xff1f; Android SDK自带的压力测试工具&#xff0c;也是一个命令行工具。它向系统发送伪随机的用户事件流&#xff08;如按键输入&#xff0c;触摸屏输入&#xff0c;手势输入等&#xff09;&#xff0c;实现对正在开发的应用程序进行压力测试。 &#xff0…

Flink在美团的应用与实践听课笔记

本文系《Flink在美团的应用与实践》的听课笔记 原始视频视频资源已经在优酷公开&#xff1a;2018.8.11 Flink China Meetup北京站-Flink在美团的应用与实践 作者&#xff1a;刘迪珊美团 1.现状和背景 实时平台架构 最底层是数据缓存层&#xff0c;可以看到美团测的所有日志类…

[LeetCode]题解(python):150-Evaluate Reverse Polish Notation

题目来源&#xff1a; https://leetcode.com/problems/evaluate-reverse-polish-notation/ 题意分析&#xff1a; 给定一个数组&#xff0c;用这个数组来表示加减乘除&#xff0c;例如 ["2", "1", "", "3", "*"] -> ((2 …

微软苹果服务器宕机,苹果服务器宕机,iPhone用户别做这两项操作,微软特斯拉也中招...

原标题&#xff1a;苹果服务器宕机&#xff0c;iPhone用户别做这两项操作&#xff0c;微软特斯拉也中招虽然苹果一直都以安全来标榜自己&#xff0c;而事实上也确实如此。IOS封闭的环境&#xff0c;相对与安卓这个开放的环境确实要更加安全一些。苹果可以很好的抵御外来的风险&…

索尼发布无人机相机专利,支持眼部对焦

无人机将采用可折叠式设计&#xff0c;无需使用手机就能操控。 目前消费级无人机的行业霸主自然是大疆无疑&#xff0c;前段时间推出的Mavic 2再次让我们领略了大疆无人机的实力。不过近日&#xff0c;索尼在日本公布了其首个无人机相机专利技术&#xff0c;似乎在向大疆发起挑…

你需要知道的高性能并发框架Disruptor原理

Disruptor的小史 现在要是不知道Disruptor真的已经很outer了&#xff0c;Disruptor是英国外汇交易公司LMAX开发的一款开源的高性能队列&#xff0c;LMAX Disruptor是一个高性能的线程间消息传递库&#xff0c;它源于LMAX对并发性&#xff0c;性能和非阻塞算法的研究&#xff0…

c++11 多线程 1c++ concurrency in action

一、并行、多线程 1、计算机中的并行有两种方式&#xff1a;任务切换、利用多处理器多核。 纯粹的任务切换&#xff1a; 纯粹的多处理器多核&#xff1a; 任务切换与多处理器多核结合&#xff1a; 实际应用中是“任务切换与多处理器多核结合”方式&#xff0c;首先现在硬件偏移…

芯片刀片服务器,使用“刀片服务器”其实不难

刀片服务器已经轰轰烈烈地吵了将近两年的时间&#xff0c;市场上的刀片服务器产品也越来越多&#xff0c;所使用的芯片种类也逐渐发展为intel、amd、power等几种&#xff0c;支持的平台也包括了unix和ia架构。2005年底&#xff0c;hp还推出了基于安腾2平台的bl60p产品&#xff…

Prometheus 对比 Zabbix

公司要上监控&#xff0c;Prometheus 是最热门的监控解决方案&#xff0c;作为喜新厌旧的程序员&#xff0c;我当然是选择跟风了&#xff0c;但上级更倾向于 Zabbix&#xff0c;那没办法&#xff0c;只能好好对比一番&#xff0c;给出几个靠谱的理由了。 但稍稍深入一点&#x…

好理解的Java内存虚假共享(False Sharing)性能损耗以及解决方案

虚假共享(False Sharing)也有人翻译为伪共享 参考 https://en.wikipedia.org/wiki/False_sharing 在计算机科学中&#xff0c;虚假共享是一种性能降低的使用模式&#xff0c;它可能出现在具有由高速缓存机制管理的最小资源块大小的分布式一致高速缓存的系统中。当系统参与者将…

delphi xe 文件服务器,DelphiXE7中创建WebService(服务端+客户端)

相关资料&#xff1a;http://www.2ccc.com/news/Html/?1507.htmlhttp://www.dfwlt.com/forum.php?modviewthread&tid922DelphiXE7新建WebService具体操作&#xff1a;1.打开“DelphiXE7”->“File”->“New”->“Other”2.“New Items”&#xff0d;>“Delph…

Android app 别用中文名

/************************************************************************** Android app 别用中文名* 说明&#xff1a;* 本来想分析一下这份源代码&#xff0c;结果发现因为项目名中有中文不能自动生成R* 文件&#xff0c;于是不想分析了。** …