本文是 《软件架构概念介绍(一)》 的下半部分,原文某些概念使用了.NET讲解,由于对.NET不熟悉,可能理解的不是很透彻,整体读下来,个人觉得软件架构概念还是很复杂的,后面会用Python或PHP再进行详细的学习,修炼内功,什么时候都不晚。
7:关联(Association)
描述两个类之间关系相对通用的术语是关联,而聚合和组合是相对特殊的概念。
关联表示两个类之间是 has-a 关系,类之间没有特定的所有权,它只是连接两个类。
聚合(Aggregation)是一种弱类型的关联,两个类之间有部分的所有权。 使用术语 uses 来暗示弱 has-a 关系。 比如一个学校某个部门聚合了教师,教师实际上属于很多部门,如果某个部门不存在了,教师还存在。
组合(Composition )是一种强类型的关联,某个类拥有另外一个类完整的所有权。一般使用 owns 来暗示强关系的 has-a ,比如学校某个部门教授一门课程,如果这个部门消失了,那么课程也就没了。
总体来说聚合是一种特殊的关联,而组合是一种特殊的聚合,Association->Aggregation->Composition。
8:抽象和泛化
抽象强调理念、属性而不是细节,抽象的重要性来源于其可以隐藏不相关细节,以及通过名称引用对象的能力。
抽象在项目中至关重要,强调对象是什么或做什么,并不强调如何工作的,它是管理大型程序复杂性的主要手段。
抽象是通过隐藏不相关细节降低复杂性,泛化是通过替换多个entities(它们拥有相同的结构,且执行类似的功能)降低复杂性的。泛化通过拥有更大领域对象(相同类型或不同类型)来扩展应用程序,编程语言通过变量,参数化,泛型和多态性提供泛化。它强调对象之间的相似性,因此泛化通过将个体汇总到组管理复杂性,并能提供一个representative(找出组中任何一个个体)。
抽象和泛化经常一起使用,Abstracts are generalized through parameterization to provide greater utility。在参数化中,entity中一个或多个部分被替换成一个指向entity的新名称,新名称就是parameter,当parameterized abstract被调用的时候,将绑定 parameter 到 argument 。
9:什么是抽象类
一个类如果前面有abstract关键字,那么它就是抽象类,抽象类不能实例化,它只能是其它类的超类。
抽象类是一个概念,子类必须实现抽象类,一个类只能继承于一个抽象类(但能实现多个接口)。
一个实现类必须覆盖(override)所有申明为abstract的methods/properties,同时也必须覆盖所有虚methods/properties。
抽象类是实现框架非常好的一种方法。
10:接口
接口通过定义结构,拆分了具体实现,当你需要实现更加的通用(实现可改变),那么接口非常有用,除此之外如果实现修改非常频繁,接口也非常有用。
题外话,有些人说用接口的形式定义所有类,但我不推荐这种方式,多态和接口应该是一种面向对象编程思想,而不是语言属性,比如Python中就没有原生支持多态和接口,这说明什么?
接口用来定义一个通用模版,然后一个或多个抽象类去定义这个接口的局部实现,接口仅仅指定方法定义(隐式公开或抽象),包含属性properties(隐示公开或抽象)。
接口使用keyword关键字申明,接口和抽象类一样不能实例化。
假如一个类实现一个接口,但没有定义(不是申明)所有的方法,那么它就是一个抽象类,所有的方法定义必须由抽象类的子类来扩展,除此之外接口可以从其他接口继承。
11:接口和类的区别
在某些语言中,一个类能够实现一个接口,也能够实现多个接口,当类实现一个接口时,这个类的对象就封装在这个接口内部,比如MyLogger是一个类,同时它实现了ILogger接口,那它可以这样写:
ILogger log = new MyLogger();
从概念上来说,接口和类是完全不同的,从理论上来说,类强调封装的概念,而接口强调抽象的概念(隐藏实现细节),它们之间有着明显的不同,因此比较它们非常困难且没有意义,而接口和抽象类之间可以进行比较(见下面)。
12:抽象类和接口之间的区别
抽几个觉得重要的说明:
抽象类让你定义一些行为,它强迫你的子类提供其他的行为,举个例子,假如你有一个应用程序框架,一个抽象类用来给服务提供默认的实现和一些必须的模块,比如事件记录和消息处理,这种方法允许开发者在这个框架的指导下开发应用程序。
然后,在具体实践中,当遇到一些应用程序特定功能时,只有你的程序能执行,比如启动或暂停工作。抽象类能够定义一些虚拟的启动或暂停方法,基类知道它需要这些方法,但一个抽象类让你的类承认它不知道如何执行这些动作,它只知道必须启动这些动作,当到了启动的时候,抽象类能够调用启动方法,但基类调用这个方法的时候,它能够执行子类定义的方法。
13:继承
我们知道抽象和泛化之间关系紧密,继承和特化同样如此,将这两个概念和泛化放在一起讨论很重要,这样能够更好的明白并减低复杂性。
通过扩展现有类来创建新类的能力称为继承。
现实世界中对象之间最重要的关系是特化(specialization),它能表示为 is-a 的关系。当说狗是一种动物,表示狗是动物的一种特化,它有动物的所有特征,但它也特化出一些家犬特有的特征;猫也是哺乳动物,它和够一样分享一些相同的特征,但它也具有一些猫的特殊特征。
特化和泛化之间的关系是相互倒立且分层的,泛化的另外一面就是泛化,哺乳动物在猫和狗之间泛化了一些特征,但它们也特化了一些特殊的特征。
举个例子,IOException和SecurityException是Exception的子类型,它们有Exception所有的特征和行为,这表示IOException是Exception的一种特化,SecurityException也是一种Exception,我们希望它和IOException一样共享Exception所有特征。但IOException和SecurityException也特化了Exception,拥有自己独特的特征。
在OOP中,特化关系使用称为继承的原则实现,这是最常用,也是最自然,最广泛被接收的特化实现方式。
14:多态
多态是一种通用术语,表示“多种形状”,确切的说多种不同型态的事物能够执行相同的操作。
有时我认为理解面向对象编程非常困难,因为它们有四个主要的概念,同时每个概念之间关系很密切。一次我们必须小心的独立理解每个概念,同时明白和其他概念之间的关系。
在OOP中,多态有多种不同的技术实现,分别是方法重载(overloading),操作符重载,方法覆盖(overriding)。
15:方法重载
方法重载可以使用相同的名字定义多个方法,比如:
public class MyLogger { public void LogError(Exception e) { // Implementation goes here } public bool LogError(Exception e, string message) { // Implementation goes here } }
16:操作符重载
它是多态的一种特殊情况,某些运算符(比如+,-,==)称为多态函数,因此具有不同的行为(具体取决于其参数的类型)。
17:方法覆盖
方法重写是一种语言功能,它允许子类覆盖超类已经实现的方法。
一个子类可以指定它自己的实现,但需要和超类具有相同的signature,这意味着当重写方法时,子类的方法必须具有与超类具有相同的名称和参数列表。
18:Use Case,Class Diagram,Package Diagram,Sequence Diagram
这些都是UML中的概念,后续需要看一些专业的资料才能掌握,目前看了这篇文章后,完全无感。
19:二层和三层架构
二层架构已经很古老了,三层架构其实也已经不适合目前的技术潮流了,但仍然有一定的代表性,包含三个部分:
20:MVC架构
Model-View-Controller (MVC)架构拆分domain,presentation,actions三种模型到三个独立的类中。
不幸的是,这种模式的普及导致了一些错误的用法,每种技术(PHP,.NET)都有自己的方式定义它,从而很难理解。尤其“控制器”这个术语在不同的上下文中表示不同的事物。
下面这个是.NET中的描述方式:
21:SOA
面向服务的架构本质上是一组服务,服务之间互相通信,通信可以是简单的数据传输,或者协调两个或多个服务之间的活动,需要一些方法连接这些服务。
SOA用来连接多个系统以提供服务。SOA其实就是微服务的前身。
22:Gang of Four (GoF) Design Patterns
四人帮模式被认为是其他模式的基础,它们被分为三组(Creational, Structural, and Behavioral),好吧,我是记不下来。