原文链接:http://space.itpub.net/13164110/viewspace-704885
什么是抽象呢?首先不必澄清什么是抽象,而从什么算抽象说起,稳定的、高层的就代表了抽象。就像一个公司,最好保证了高层的稳定,才能保证全局的发展。在进行系统设计时,稳定的抽象接口和高层逻辑,也代表了整个系统的稳定与柔性。兵熊熊一窝,将良良一窝,软件的构建也正如打仗,良好的设计都是自上而下的。而对具体的编程实践而言,接口和抽象类则代表了语言层次的抽象。
追溯概念的分析,一一过招,首先来看依赖于具体,如图3-3所示。
因此,为了分离这种紧耦合,最好的办法就是隔离,引入中间层来分离变化,同时确保中间层本身的稳定性,因此抽象的中间层是最佳的选择(如图3-4所示)。
以例而理,从最常见的服务端逻辑举例,如下所示:
public interface IUserService
{
}
public class UserService : IUserService
{
}
如果依赖于具体:
public class UserManager
{
private UserService service = null;
}
或者依赖于抽象:
public class UserManager
{
private IUserService service = null;
}
二者的区别仅在于引入了接口IUserService,从而使得UserManager对于UserService的依赖由强减弱。然而对于依赖的方式并非仅此一种,设计模式中的智慧正是通过各种编程技巧进行依赖关系的解耦,值得关注和学习,后文将对设计模式进行概要性的讨论。
对WCF熟悉的读者一定不难看出这种实现方式如此类似于WCF的推荐模式,也是契约编程的基本思想。关于WCF及SOA的相关内容,我们在后文进行了相关的讨论。
总结一番,什么是抽象,什么是具体?在作者看来,抽象就是系统中对变化封装的战略逻辑,体现了系统的必然性和稳定性,能够被具体层次复用和覆写;而具体则包含了与具体实现相关的逻辑,体现了系统的动态性和变动性。因此,抽象是稳定的,而具体是变动的。
Bob大叔在《Agile Principles, Patterns, and Practices》一书中直言,程序中所有的依赖关系都应终止于抽象类或者接口,就是对面向抽象编程一针见血的回应,其原因归根到底源自于对抽象和具体的认知和分解:关联应该终止于抽象,而不是具体,保证了系统依赖关系的稳定。具体类发生的修改,不会影响其他模块或者关系。那么如何做到这种理想的依赖于抽象的设计呢?
层次清晰化
将复杂的问题简单化,是人类思维的普世智慧,也自然而然是实现软件设计的基本思路。将复杂的业务需求通过建模过程的抽象化提炼,去粗取精,去伪存真,凡此种种。而抽象的过程,其目标之一就是形成对于复杂问题简单化的处理过程,只有形成层次简单的逻辑才能将复杂需求中的关系梳理清晰,而依赖的本质正如上文所言,不就是处理关系吗?
所以,清晰的层次划分,进而形成的模块化,是实现系统抽象的必经之路。
分散集中化
由需求而设计的过程,就是一个分散集中化的过程,把需求相关的业务通过开发流程的需求分析过程进行整理,逐步形成需求规格说明、概要设计和详细设计等基本流程。分散集中化,是一个梳理需求到形成设计的过程,因此对于把握系统中的抽象和具体而言,是一个重要的分析过程和手段。现代软件工程已经对此形成了科学的标准化流程处理逻辑,例如可以借助UML更加清晰地设计流程、分析设计要素,进行标准化沟通和交流。
具体抽象化
将具体问题抽象化,是本节关注的要点,而处理的方法是什么呢?答案就在设计模式。设计模式是前辈智慧的总结和实践,所以熟悉和学习设计模式,是学习和实践设计问题的必经之路。然而,没有哪个问题是由设计模式全权解决,也没有哪个模式能够适应所有的问题,因此要努力的是尽量积累更多的模式来应对多变的需求。作为软件设计话题中最重量级的话题,关注模式和实践模式是成长的记录。
封装变化点
总的来说,抽象和变化就像一对孪生兄弟,将具体的变化点隔离出来以抽象的方式进行封装,在变化的地方寻找抽象是面对抽象最理想的方式。所以,如何去寻找变化是设计要解决的首要问题,例如工厂模式的目标是封装对象创建的变化,桥接模式关注对象间的依赖关系变化等。23个经典的设计模式,从某种角度来看,正是对不同变化点的封装角度提出的不同解决方案。
这一设计原则还被称为SoC(Separation of Concerns)原则,定义了对于实现理想的高耦合、低内聚目标的统一规则。
3.设计的哲学
之所以花如此篇幅来讲述一个看似简单的问题,其实最终理想是回归到软件设计目标这个命题上。如果悉心钻研就可发现,设计的最后就是对关系的处理,正如同生活的意义在于对社会的适应一样。因此,回归到设计的目标上就自然可知,完美的设计过程就是对关系的处理过程,也就是对依赖的梳理过程,并最终形成一种合理的耦合结果。
所以,面向对象并不神秘,以生活的现实眼光来看更是如此。把面向对象深度浓缩起来,可以概括为:
目标:重用、扩展、兼容。
核心:低耦合、高内聚。
手段:封装变化。
思想:面向接口编程、面向抽象编程、面向服务编程。
其实,就是这么简单。在这种意义上来说,面向对象思想是现代软件架构设计的基础。下面以三层架构的设计为例,来进一步感受这种依赖哲学在具体软件系统中的应用。关于依赖的抽象和对变化隔离的基本思路,其实也是实现典型三层架构或者多层架构的重要基础。只有使各个层次之间依赖于较稳定的接口,才能使得各个层次之间的变化被隔离在本层之内,不会造成对其他层次的影响,这完全符合开放封闭原则追求的优良设计理念。将这种思路表达为设计,可以表示为如图3-5所示的形式。
由图3-5可知,IDataProvider作为隔离业务层和数据层的抽象,IService作为隔离业务层和表现层的抽象,保证了各个层次的相对稳定和封装。而体现在此的设计逻辑,就正是对于抽象和耦合基本目标概念的体现,例如作为重用的单元,抽象隔离保证了对外发布接口的单一和稳定,所以达到了最高限度的重用;通过引入中间的稳定接口,达到了不同层次的有效隔离,层与层之间体现为轻度耦合,业务层只持有IDataProvider就可以获取数据层的所有服务,而表现层也同样如此;最后,这种方式显然也直接实践了面向接口编程,面向抽象编程的经典理念。
同样的道理,对于架构设计的很多概念,放大可以扩展为面向服务设计所借鉴,放小这正是反复降调的依赖倒置原则在类设计中的基本思想。因此,牢记一位软件大牛的说法:软件设计的任何问题,都可以通过引入中间逻辑得到解决。而这个中间逻辑,很多时候被封装为抽象,是最为合理和智慧的解决方案。
让我们再次高颂《老子》的小国寡民论,来回味关于依赖哲学中,如何实现更好的和谐统一以及如何遵守科学的软件管理思想:邻国相望,鸡犬之声相闻,民至老死,不相往来。
特别说明:本人转载文章纯为技术学习,总结经验,并无其他目的,若有他人继续转载,请链接原作者的地址,而不是本文的地址,以示对作者的尊重。最后对原作者的辛勤劳动表示感谢!
分享到:
相关推荐
此资源出自下面的作者,我只是转载,非常实用的设计方法,如果您想成位出色的设计师,那就再复习复习吧!如果您想成为软件设计师,通过学习,您将会站另一个高度看待软件设计. 原始地址:...
数据模型设计是数据库系统开发中的核心环节,它用于抽象和描述现实世界的数据结构,以便于计算机存储和处理。本文将围绕“数据模型设计心得”展开,结合提供的文档资源,探讨在广告平台项目系统和会员模块设计中如何...
【Zigbee组网分析】 Zigbee是一种短距离、低功耗的无线通信技术,广泛应用于物联网设备的连接和数据传输。...在实际应用中,开发者需要根据具体需求调整和优化这些步骤,以实现最佳的网络性能和用户体验。
官网介绍说,hal(hardware abstract layer)是一层硬件的抽象,看到这里,我非常激动,看来st终于意识到原来标准库的问题了,原来的标准库非常依赖于具体硬件细节,很难体现出使用库的优势,而且很难移植。...
在Java中,抽象可以通过抽象类和接口来实现,使开发者能够定义类的骨架而不必提供具体实现,从而促进代码的复用和模块化设计。 2. **继承**:继承支持类之间的层次结构,允许创建的新类(子类)继承现有类(父类)...
- 抽象类适合于提供部分实现,而具体实现留给子类完成。 - 在设计时考虑未来可能的变化,以便于扩展而不影响现有代码。 #### 六、资源泄露与垃圾回收 **6.1 资源泄露** - 避免资源泄露,特别是对文件和网络连接的...
将书本上关于文本分类的相关内容,如分类器、特征词选择算法等,用程序实现,让入门者对文本分类有个感性的、具体的了解,毕竟数学公式还是蛮抽象的; 2.“尽信书不如无书”,“纸上得来终觉浅,绝知此事要躬行”,...
理论知识可能在课本上显得抽象,但在实际操作中,它们会变得具体且实用。例如,血液生化指标的学习在理论上可能枯燥,但在实际检测中,这些指标能够直接影响医生对疾病的诊断和治疗方案的制定。 实习不仅是技能的...
- **抽象**:抽象是提取对象共性的过程,包括数据抽象和行为抽象。它关注对象的属性和行为,而不涉及具体实现。 - **继承**:继承允许子类从父类继承特性,增强代码的扩展性,降低软件的复杂性。 - **封装**:...
总之,Linux字符设备驱动是连接用户空间应用和硬件设备的桥梁,它实现了设备的抽象化,使得应用程序可以以统一的方式与各种不同的硬件设备进行交互。理解并掌握字符设备驱动的原理和实现方法,对于进行系统级编程和...
FragmentActivity是Android Support Library中的一个抽象类,它是Activity的扩展,提供了对Fragment的支持。使用FragmentActivity,开发者可以轻松地在同一个界面上添加、移除或替换多个Fragment。下面我们将详细...
C语言则提供了更高级别的抽象,使得程序更易理解和维护。实例中,两种语言的使用都有所涉及,可以帮助你根据项目需求选择合适的编程语言。 在实例中,你将接触到51单片机的中断系统,它是处理突发事件的重要机制。...
Java中的`java.io.File`类是用于表示文件和目录路径名的抽象表示。它提供了许多操作,如创建、删除、重命名文件和目录,以及获取文件属性等。 2. **路径构造**: 可以通过传递字符串参数给File构造器来创建一个...
1. **解析器构造**: 解析器是如何工作的,包括词法分析(将输入分解为单词或符号)和语法分析(将单词或符号转换为抽象语法树)。 2. **组合解析器的概念**:如何通过组合小的解析器来构建复杂的解析逻辑,以及如何...
它包括了销售业务过程中的属性、数量、位置及其相互关系等内容的抽象表示。 **数据的表现形式**: 1. **列表形式**:如客户销售分析报表,通过表格的形式清晰地展示了不同客户在各月份的销售情况。 2. **图表形式**...
DataFrame和DataSet是Spark 2.0引入的新特性,它们提供了更高级别的抽象,使得SQL查询和类型安全的数据操作变得更加便捷。 在大数据处理中,Spark的Shuffle过程是非常关键的,它涉及到数据在集群中的重新分布,通常...
举例让抽象问题具体化 分解让复杂问题简单化 ##第五章 优化时间和空间效率 时间效率 时间效率与空间效率的平衡 第六章 面试中的各项能力 知识迁移能力 抽象建模能力 发散思维能力 leetcode 题目分享(长期更新): ...