学习笔记1

<!-- [if !mso]> <style> v/:* {behavior:url(#default#VML);} o/:* {behavior:url(#default#VML);} w/:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} </style> <![endif]--><!-- [if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning/> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL/> <w:BalanceSingleByteDoubleByteWidth/> <w:DoNotLeaveBackslashAlone/> <w:ULTrailSpace/> <w:DoNotExpandShiftReturn/> <w:AdjustLineHeightInTable/> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:UseFELayout/> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!-- [if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--><!-- [if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} </style> <![endif]-->

一.设计模式 , 并发 , 线程 , 大数据处理 .

类是 OOP 中的核心组成元素,通常都是使用类来 封装 对象(属性、行为)。在经典图书《代码大全》里定义: 创建高质量的类,第一步,可能也是最重要的一步,就是创建一个 好的接口 。这也包括了创建一个可以通过接口来展现的合理的抽象,并确保细节仍被隐藏在抽象背后

但要记住的是 面向对象编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。 在实际的项目中如果能够做到这样也就够了,类的划分还得根据实际的需求而定

习之以恒

Research shows that it takes “about ten years, or ten to twenty thousand hours of deliberate practice” to become an “expert.” That’s a lot of time. Furthermore, becoming an expert does not always mean doing the same task for 10 years; it often means doing a wide variety of tasks within a particular domain for 10 years. It will take a lot of time and energy to become an “expert”; working as a developer for a few years is not enough. Want to become a senior developer in your early 30s? Either start your education/training sooner or be willing to do a lot of work, reading, and practicing in your spare time. I started programming in high school, and I devoted a lot of off-hours to keeping up with the industry, learning new skills, and so on. As a result, I hit the intermediate and senior level developer positions significantly earlier in my career than most of my peers, which translates to an awful lot of money over time.

研究表明,要成为一名“专家”,需要花费 10 年,或者 1000020000 小时的深入练习时间。真的很久。还有,成为专家并不一定代表 10 年内执行同样的任务;通常这意味着要在特定领域内执行广泛的任务 10 年 。要成为“专家”需要花费大量的时间和精力;做几年开发人员是不够的。想在 30 岁左右成为一名高级软件开发工程师?要么尽早开始接受教育 / 培训,要么你得愿意用业余时间进行大量的工作、阅读和练习 。我从高中开始编程,还牺牲了许多休息时间去跟踪行业发展、学习新技能等等 。结果,我获得中级和高级开发人员职位的时间就比我的大部分同事都要早得多,随着时间的推移,这些就转化成为非常多的收入。

见山只是山 见水只是水 —— 提升对继承的认识

作者:温 昱 本文发布于《 CSDN 开发高手》

封装、继承、多态是 OO 的三大特性,由此可见继承思想的重要性。但是,不少人对继承的理解过多地局限在 OOP 层面,从而限制了继承思想在 OOD 层面的巨大作用。笔者认为,软件工程师应该不断提升对 OO 思想的认识层面,加强实际开发能力。

本文站在 OOD 的角度,将继承看成实现 OOD 的强大手段,通过具体例子,说明针对接口编程( Program To An Interface )、混入类( Mix In Class )、基于角色的设计( Rolebased Design )这三个与继承紧密相关的著名 OOD 技巧。

一、从一则禅师语录说起

《五灯会元》卷十七中,有一则青原惟信禅师的语录: 老僧三十年前未参禅时,见山是山,见水是水。及至后来亲见知识,有个入处,见山不是山,见水不是水。而今得个休歇处,依前见山只是山,见水只是水。

禅师高论,颇具哲理,讲的是悟道的过程。其实,领悟 OOD 之道的过程又何尝不是如此呢?

1 、见继承是继承——程序员境界

初学 OOP 的人,大多处在 见继承是继承 的层面,最关心的是类的语法、类的成员变量、类的成员函数等这些实现层的东西。这是程序员境界。

2 、见继承不是继承——成长境界

开始研习 OOD 之时,又往往跳到另一个极端,只关心设计,而无心(也可能是无力)关心实现,处在所谓 见继承不是继承 的层面。在这个阶段的人,脑中的兴奋点是 设计 ,是职责分配、接口设计、可重用性、可扩展性、耦合度、聚合度等这些设计层的概念。这是成长境界。

3 、见继承只是继承——设计师境界

学通 OOD 之后,会达到 见继承只是继承 的层面。一个 字,体现了继承背后的 设计理念 才是该境界的要害。但是,这个阶段和第二阶段不同,第二阶段是一味的否定,而本阶段是否定之否定,把 OOP 层面的继承机制看成用来实现特定 OOD 的手段加以利用。这是设计师境界。

二、从 OOD 层面认识继承

OOP 层面,除了类、成员变量、成员函数这些最基本的概念,最重要的就是代码重用和名字空间的可见性了。而 OOD 层面,最基本的概念是类、职责、状态、角色这些更抽象一级的概念,及其相关的耦合度、聚合度、可重用性、可扩展性、可维护性等。可见,虽然 OOD 最终要依赖 OOP 作为实现手段,但显然 OODOOP 并非在同一抽象级上,有不同的概念体系和思维方式。

再说继承。单纯从 OOP 层面看,继承是一个通过复用父类功能而扩展应用功能的基本机制,它允许你根据旧的类快速定义新的类;还有些人用继承仅为了获取名字空间的可访问性。但是,从 OOD 层面看,继承可以演变出 “Is-A”“Plays Role Of” 等抽象的设计概念。因此,担任设计师角色的人如果自己还限制在 OOP 的层面, 设计乏术 的局面是不可避免的。总之,提升对继承的认识,对活用接口继承和实现继承这两种继承机制来实现 OOD 意图非常重要。

与继承相关的 OOD 技巧有很多, 本文仅讨论比针对接口编程、混入类、基于角色的设计这三种技巧 ,下图展示了它们和继承的关系。


三 、针对接口编程 —— 隔离变化

1 、相关理论

耦合是依赖的同义词,被定义为 两个元素之间的一种关系,其中一个元素变化,导致另一个元素变化 。抽象耦合被定义为 若类 A 维护一个指向抽象类 B 的引用,则称类 A 抽象耦合于 B”

依赖性倒置原则( Dependency Inversion Principle )形式化了抽象耦合的概念,明确表述了应该 依赖于抽象类,不要依赖于具体类

针对接口编程遵守上述原则,从而在很大程度上阻止了变化波及范围的扩大,有效地隔离了变化,有助于增强系统的可重用性和可扩展性。

2 、 针对接口编程举例——用于体系结构设计

根据经典的 CoadOOD 理论,一个项目通常包含四个层:用户界面层、问题领域层、数据管理层、系统交互层,如下图所示。


将体系结构划分为层的一个很大好处是,这些层形成了开发小组的自然分界 —— 每层的开发人员所需要的技巧是不同的。用户界面层的开发小组需要了解将使用的用户界面工具包;数据管理层的开发小组需要熟悉相关的数据库、持久工具或者使用的文件系统;系统交互层的开发小组需要了解通讯协议和用到的中间件产品;问题领域层的开发小组不需要了解这些知识,他们需要最深的领域知识,以及用到的相关分布对象或组件技术。

但是,要真正使得各个开发小组最大限度地独立开发,还需要一个稳定的体系结构设计做保证才行,其设计的核心思想是:问题领域层 不依赖于 其他任何层,而其他任何层 只依赖于 问题领域层。如下图所示。


该体系结构设计的实现,极为重要的一点,就是要使用针对接口编程的技巧。以系统交互层对问题领域层的单向依赖为例:

Ø 如果系统交互层要调用问题领域层的操作,直接调用即可。

Ø 如果问题领域层要调用系统交互层的操作,需要由问题领域小组定义一个通用的抽象接口,通过针对接口编程调用这个抽象接口;而系统交互小组通过接口继承机制,定义抽象接口的子类,该子类完成抽象接口的具体实现。

笔者曾有一个项目,该系统需要实时地将本系统的数据变化,通知远端的另一个系统。相关设计如下图所示。在问题领域层,仅包含了一个抽象接口 CChangeReporter ,而并不关心 CChangeReporter 的具体实现。系统交互层拥有选择具体实现方法的自由,比如 CSoapChangeReporter 是用 SOAP 通讯协议实现的 CChangeReporter CTcpChangeReporter 是用 TCP 协议实现的 CChangeReporter 。而且假设由于技术的或商业的原因,将来需要同时支持多种通讯协议,也比较容易。


3 、针对接口编程举例——用于类设计

笔者曾在《运用设计模式设计 MIME 编码类》一文中,详述了如何使用策略模式来设计一个可重用、易扩充的 MIME 类层次,其中抽象接口类 CMimeAlgo 起到了至关重要的作用,现简述如下。

用户通过 CMimeString 使用 MIME 编码的功能, CMimeString 允许用户在运行过程中动态配置 MIME 编码的具体算法;具体 MIME 编码算法由 CMimeAlgo 类层次提供,具体的 CMimeAlgo 子类的实例化是由 CMimeString 根据用户的配置动态完成的;要增加新的 MIME 编码算法,只需实现新的 CMimeAlgo 子类,并简单扩充 CMimeString 的动态实例化代码即可。如下图所示。


四、 混入类 —— 更好的重用性

1 、相关理论

混入类被定义为 一种被设计为通过继承与其他类结合的类 ,它给其他类提供可选择的接口或功能 。

从实现上讲,混入类要求多继承;混入类通常是抽象类,不能实例化。

混入类的作用在于:它不仅可以提高功能的重用性,减小代码冗余;而且还可以使相关的“行为”集中在一个类中,而不是分布到多个类中,避免了所谓的“代码分散”和“代码交织”问题,提高了可维护性。

2 、混入类举例

来看一个具体项目。在一个信用卡客户服务系统项目中,要求能够以多种方式发送多种信息给用户,并能够适应未来业务的发展变化。

当前系统需要支持的发送方式:

Ø 打印(并邮寄)

Ø Email

Ø 传真

可预见的未来要支持的发送方式:

Ø 手机短信

Ø PDA 消息

当前系统需要支持的待发送信息:

Ø 信用卡对账单

Ø 信用卡透支催收单

可预见的未来要支持的待发送信息:

Ø 信用卡新业务宣传单

Ø 信用卡促销活动宣传单

下面是一些设计考虑。一种发送方式要支持多种待发送信息,我们希望发送功能有很好的可重用性;为了方便未来加入对新的发送方式和发送信息的支持,设计必须具有良好的可扩展性。相关设计如下图所示。其中采用了混入类的 OOD 技巧,用一个 CSendableDoc 作为混入类,支持发送功能的重用; CSendalbeDoc 还采用了策略模式支持发送方式的扩充 。


五、 基于角色的设计 —— 使用角色组装协作

1 、相关理论

协作被定义为“多个对象为了完成某种目标而进行的交互”。角色被定义为“特定协作中的对象的抽象”,它“仅定义了对象特征的一个对某协作有意义的子集”。协作和角色的概念和现实世界很接近,比如下图中, Jane 教授扮演三个角色——母亲、妻子、教授。


接口分离原则( Interface Separation Principle )信奉“多个专用接口优于一个单一的通用接口”的思想,因为“任何接口都应当具有高内聚性”,以便“保证实现该接口的类的实例对象可以只呈现为单一的角色”。

基于角色的设计的意义在于:我们很容易通过已有角色的组合来构造新的协作,以完成新的功能。而且,从 UML 类图可以很自然地导出基于角色的设计方案,例如:


从上面的类图很自然地导出下面的设计:


2 、基于角色的设计举例

比如,待开发的一个系统,其后台数据源可能是关系数据库、一般的文件、还可能是另一个私有数据库。既然接口可以隔离变化,我们可以定义一个单一的接口,为所有的数据客户类提供服务。如下图所示。


但是,上面的设计违背了基于角色的设计思想,根本不能保证 实现该接口的类的实例对象可以只呈现为单一的角色 ,这会带来一些问题。比如,有一个数据客户类,不需要插入、更新等功能,而仅仅需要对数据进行读操作,这时显然一个提供 服务的 角色 是最合理的设计,但 CRowSetManager 却是如此之 的一个接口。最终,我们可以这样来改进设计,如下图所示。


接口的本质

1 )接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界 如果你是 …… 则必须能 ……” 的理念。

2 )这里该选抽象类还是接口呢?还记得第一篇文章我对抽象类和接口选择的建议吗?看动机。这里, 我们的动机显然是实现多态性而不是为了代码复用,所以当然要用接口

3) 此乃面向接口思想一大作用: 使相互不认识的类进行交互 。这样做是很有好处的,首先它们之间的耦合度大大降低,其次双方都可以进行替换,只要实现了相同的接口,就没有问题。

课题 :

面向接口编程详解(二) —— 编程实例

通过上一篇文章的讨论,我想各位朋友对 面接接口编程 有了一个大致的了解。那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象。为充分考虑到初学者,所以这个例子非常简单,望各位高手见谅 。

问题的提出

定义: 现在我们要开发一个应用,模拟移动存储设备的读写,即计算机与 U 盘、 MP3 、移动硬盘等设备进行数据交换。

上下文(环境): 已知要实现 U 盘、 MP3 播放器、移动硬盘三种移动存储设备,要求计算机能同这三种设备进行数据交换,并且以后可能会有新的第三方的移动存储设备,所以计算机必须有扩展性,能与目前未知而以后可能会出现的存储设备进行数据交换。

各个存储设备间读、写的实现方法不同, U 盘和移动硬盘只有这两个方法, MP3Player 还有一个 PlayMusic 方法。

名词定义: 数据交换 ={ 读,写 }

看到上面的问题,我想各位脑子中一定有了不少想法,这是个很好解决的问题,很多方案都能达到效果。下面,我列举几个典型的方案。

解决方案列举

方案一: 分别定义 FlashDiskMP3PlayerMobileHardDisk 三个类,实现各自的 ReadWrite 方法。然后在 Computer 类中实例化上述三个类,为每个类分别写读、写方法。例如,为 FlashDiskReadFromFlashDiskWriteToFlashDisk 两个方法。总共六个方法。

方案二: 定义抽象类 MobileStorage ,在里面写虚方法 ReadWrite ,三个存储设备继承此抽象类,并重写 ReadWrite 方法。 Computer 类中包含一个类型为 MobileStorage 的成员变量,并为其编写 get/set 器,这样 Computer 中只需要两个方法: ReadDataWriteData ,并通过多态性实现不同移动设备的读写。

方案三: 与方案二基本相同,只是不定义抽象类,而是定义接口 IMobileStorage ,移动存储器类实现此接口。 Computer 中通过依赖接口 IMobileStorage 实现多态性。

方案四: 定义接口 IReadableIWritable ,两个接口分别只包含 ReadWrite ,然后定义接口 IMobileStorage 接口继承自 IReadableIWritable ,剩下的实现与方案三相同。

下面,我们来分析一下以上四种方案:

首先,方案一最直白,实现起来最简单,但是它有一个致命的弱点:可扩展性差。或者说,不符合 开放 - 关闭原则 (注:意为对扩展开放,对修改关闭)。当将来有了第三方扩展移动存储设备时,必须对 Computer 进行修改。这就如在一个真实的计算机上,为每一种移动存储设备实现一个不同的插口、并分别有各自的驱动程序。当有了一种新的移动存储设备后,我们就要将计算机大卸八块,然后增加一个新的插口,在编写一套针对此新设备的驱动程序。这种设计显然不可取。

此方案的另一个缺点在于,冗余代码多。如果有 100 种移动存储,那我们的 Computer 中岂不是要至少写 200 个方法,这是不能接受的!

我们再来看方案二和方案三,之所以将这两个方案放在一起讨论,是因为他们基本是一个方案(从思想层面上来说),只不过实现手段不同,一个是使用了抽象类,一个是使用了接口,而且最终达到的目的应该是一样的。

我们先来评价这种方案:首先它解决了代码冗余的问题,因为可以动态替换移动设备,并且都实现了共同的接口,所以不管有多少种移动设备,只要一个 Read 方法和一个 Write 方法,多态性就帮我们解决问题了。而对第一个问题,由于可以运行时动态替换,而不必将移动存储类硬编码在 Computer 中,所以有了新的第三方设备,完全可以替换进去运行。这就是所谓的 依赖接口,而不是依赖与具体类 ,不信你看看, Computer 类只有一个 MobileStorage 类型或 IMobileStorage 类型的成员变量,至于这个变量具体是什么类型,它并不知道,这取决于我们在运行时给这个变量的赋值。如此一来, Computer 和移动存储器类的耦合度大大下降。

那么这里该选抽象类还是接口呢?还记得第一篇文章我对抽象类和接口选择的建议吗?看动机。这里,我们的动机显然是实现多态性而不是为了代码复用,所以当然要用接口。

最后我们再来看一看方案四,它和方案三很类似,只是将 可读 可写 两个规则分别抽象成了接口,然后让 IMobileStorage 再继承它们。这样做,显然进一步提高了灵活性,但是,这有没有设计过度的嫌疑呢?我的观点是:这要看具体情况。如果我们的应用中可能会出现一些类,这些类只实现读方法或只实现写方法,如只读光盘,那么这样做也是可以的。如果我们知道以后出现的东西都是能读又能写的,那这两个接口就没有必要了。其实如果将只读设备的 Write 方法留空或抛出异常,也可以不要这两个接口。总之一句话:理论是死的,人是活的,一切从现实需要来,防止设计不足,也要防止设计过度。

在这里,我们姑且认为以后的移动存储都是能读又能写的,所以我们选方案三。

面向对象关注什么? 关注的是对象的行为,面向对象是使用行为来对对象进行分类的 !在面向对象中派生类为什么能够替换基类(替换原则),不是因为派生类是一个基类, 而是因为派生类具有与基类一致的行为 ,在派生类与基类的行为不一致的情况下派生类仍然是一个基类(如果有人敢否认这个,大家说怎么办?旁边有人喊道:砍死他!), 但是这个时候派生类消减了基类的行为,违背了替换原则,这也是恶心设计的由来 。所以说,对于面向对象而言我们要关注 “Act As”,“Act As” 的标准来对对象进行归类,至于什么 “Is A” 之类的伪标准统统扔到它姥姥家去

设计的目的是有以下的两点:

  • 保证公司的利益
  • 取悦客户

作为一个软件设计人员,如果能够同时 站在公司的立场和客户的立场, 做一个客户和公司都满意的解决方案就是一个非常合格的设计人员了,如果还能够高瞻远瞩的规划产品的远景目标,那么这个设计师绝对可以堪称是个高手

我们要的是行为!行为由什么决定的呢?由要用你这个类的地方期望要的行为来决定的

首先我们需要考虑的问题是:鱼自己能不能决定自己能不能吃,能不能决定自己好不好吃?应该是不能吧,决定鱼能不能吃,好不好吃的应该是吃鱼的对象对吧。也许从普通人的角度来看河豚是不能吃的,但是从高明的大厨或者资深的老饕的角度来看河豚就是无比的美味了,这也是我在序章的最后专门添加一幅图片重点解说对象行为的原因

话说回来,既然鱼不能决定能不能吃、好不好吃,也就是说明能不能吃的行为不是有鱼能够决定的,那么也许有人会问那为什么要实现 IEatable 接口呢,和直接做一个属性不是一样吗?这个问题问的非常的好,确实既然鱼不能够决定自己能不能吃、好不好吃,那么为什么鱼要实现 IEatable 接口呢。 其实,在 Fish 上实现 IEatable 接口完全是出于使用方便性和接口的层次 (后续的文章中会重点讨论这个问题)来考虑的,完全面向对象的搞法应该是有另外一个对象来鉴定这个鱼是否能吃、好不好吃的(这也是基于设计的平衡来考虑的,可以参看开放 - 封闭原则)。 在这个地方使用接口和属性本质上没有什么差别,但是一旦鉴别鱼能不能吃、好不好吃的鉴别方式(实现方法)发生变化的时候,使用属性的方式就难以扩展了,只能修改代码了,但是使用接口的好处是我可以使用其它的方式补救,例如做一个实现 IEatable 接口的装饰对象来装饰鱼对象

软件设计的时候没有一味的好,也没有一味的差,任何事情都有其两面性,这个是需要取舍的,我们能够做到的事情就是让设计可控,如果设计失控了,那就全部完蛋了。话说回来,如果能够确认当前的系统中评判鱼能不能吃的标准不会发生改变,把这个不会发生改变的东西集成到鱼对象中是完全可以的,在具体的实现上用属性来实现也是一个非常不错的搞法

If 语句不超过三个是可以的,要是多于三个,你就需要处理 .

只不过希望各位时刻牢记,其实是先有了子类,才从子类泛化出父类

现代办公几乎都要用到个人计算机,个人计算机本身是一个抽象概念,台式 PC 是其中一个子类。后来,发明了笔记本电脑,我们想把笔记本电脑归为个人计算机的子类,是否合理呢?根据 LSP ,我们将台式 PC 都替换成笔记本电脑,世界应该是照常运行的(当然,实际情况可能复杂些,有些地方不能用笔记本电脑替换,但这里我们忽略这种差别)。我们办公时依赖的类是 个人计算机 ,而笔记本电脑完全可以替代这个类型而使得世界运行正常,所以,我们说将笔记本电脑归于个人计算机的子类是符合 LSP 的。

后来,又发明了转基因黄瓜,我们也想将它归到个人计算机的子类中去,行不行呢?好的,现在我们再运用 LSP ,将世界上每个依赖个人计算机的地方都替换成一根转基因黄瓜。好的,世界人民都疯了!明显这种替换会令世界运行错乱。所以,我们不能让转基因黄瓜继承个人计算机。

上面的例子是显而易见的,但有些却不那么明显。例如,现在问,兽医是医生的子类吗?这个问题,一下子还真不是很好回答,但我们可以 LSP 一下,现在,我们把医院里的医生都替换为兽医,你还敢去医院看病吗?嗯,这下子不用我多说了吧。

最后一定要说明的是, LSP 应用于程序世界和现实世界时有很大差别的,现实世界繁杂、不确定性因素多,而程序世界简单、确定。总之, LSP 就是让你记住一条,凡是系统中有继承关系的地方,子类型一定能代替父类型,而且替换后程序运行要正常。换言之,继承是一种严格的 “IS-A” 关系,也是 一般和特殊 的哲学原理在程序世界中的体现


5.1 展示了几种耦合的示例。其中汽车和交通工具属于泛化耦合,轮子和方向盘组合于汽车,汽车聚合成车队,而汽车和司机具有依赖关系。这幅图只是耦合的一个小片段,实际上,世界上各种对象形成了一张复杂的耦合网,正因为有耦合的存在,世界才能演进。正如主义哲学所说:联系是普遍的、客观的。所以,耦合的存在,有其深刻的哲学意义。

认识到上面几点对于理解对象论的世界运行理论非常重要,时刻铭记,参与真正世界运行的, 只有对象,没有类!对象在世界中,类在我们心中 !

所谓低耦合,就是先剥夺对象的选择权,再剥夺对象的感觉。对象间谁也不认识谁,只知道对象能提供什么服务

这里附带说一个问题,产生这种疑惑的原因,大多是因为朋友们已经习惯了学习一个东西时,只看其什么样子?怎么用?而不习惯于弄清楚一个东西起源于哪?出现的动机是什么?其实,要想学好、用好任何一个东西,后两个问题更关键一些。
举个例子,有人发明了吹风机,我们如果只搞清楚其是什么样子 ——“ 有个把手,有个吹风筒 ,以及怎么用 ——“ 打开按钮能吹出热风,关闭按钮就停止了 。如果我们只搞清楚这些,那么我们八成用不对这个东西,为什么?因为我们根本不知道这东西是怎么来的,它为什么要被发明出来。也许我们天天拿他吹脸取暖或吹衣服,还一派洋洋得意以为用的很好的样子。殊不知这东西其实是用来吹头发帮助头发快点干起来的。
不要笑,这种事经常发生在我们身上。因为在软件开发中,有太多的东西,我们只顾着学习其是什么样子,怎么个用法,也许就像吹风机一样,这些并不复杂,然后我们就把它用到不该用的地方,还以为自己用得很好。
用不用得好吹风机,不在于是否熟练掌握开开关关,而在于是不是用它吹头发。同理, 任何东西用得好不好,不在于是不是熟练掌握用法,而在于是不是用对了地方。而要想用对地方,就要弄清楚这个东西的 怎么出来的 出来是做什么用的

一个符合 OO 原则的、低耦合的程序世界的运作形式是这样的:首先参与运作的本质只有对象,对象不直接依赖,没有选择权,互相不知道,而只知道各个接口。客户类制定接口,对象间通过接口交互,形成运作。世界的统治者依赖注入容器决定选择哪个服务类给客户类使用

OO ,即面向对象技术,是一种旨在提高软件质量的综合性技术,其贯穿于软件系统的调研、分析、设计、开发、测试、维护、扩展、升级等整个生命周期,它包含一系列概念、思想、理论、目标、原则、实践、模式、工具、语言等要素,这些要素既相互区别又相互联系,同时从宏观和微观两个角度共同协作,指导和引导开发人员开发出高质量软件,并指导与开发有关的一切过程。

重点总结
1. 客户不会想到方方面面。
2.
有时客户并不明确自己想要什么东西,而仅仅是有个动机。
3.
不要和客户谈需求, 要谈特性
4.
开发人员 有义务引导和帮助客户挖掘系统的特性
5.
当客户描述不清某个特性时,可以采用找类似事物的方法,说 说这个特性像什么,不像什么
6.
在软件开发初期,我们需要首先整理出一张特性列表,而不是做需求分析。



重点总结
1.
高质量软件的第一要素是:软件做客户希望它做的事。
2.
在开发初期,我们要尽量站在客户角度。
3.
理解需求的最好方法是明白客户希望软件做什么。
4.
开发流程大约分为两个阶段:搞清用户想要系统做什么和迭代开发。

设计模式

1. 实践 - 总结 - 再实践 - 再总结

2. 好脑子不如烂笔头,这既是自己对之前的总结,也是将来可以看到的曾经的历程,总归是一件好事

3. 策略模式关注行为的变化 , 状态模式关注对象状态的变化如何导致对象行为的变化

4. 状态模式一个形象的比喻是 饱了睡, 饿了吃”. 在这里 饱和饿 是两种状态,” 睡和吃 是两种行为. 另外一个典型的例子是银行账户. 根据客户账户中余额的不同用户可以有不同的操作行为. 这里要注意到, 状态模式中状态与行为的对应关系. 虽然不是一一对应, 但潜藏了一些信息, 那就是实际例子中行为与状态的有限和稳定, 行为的唯一性. 有限和稳定是指对象的行为一般就那几种, 除非业务需求变动, 否则不会发生改变. 唯一性只是对象只有一个吃的行为, 二不会有第二个吃的行为.

5. 策略模式关注行为的变化 , 但归根结底只有一个行为 , 变化的只是行为的实现 . 客户不关注这些 . 当新增变化时对客户可以没有任何影响 .

状态模式同样关注行为的变化 , 但这个变化是由状态来驱动 , 一般来说每个状态和行为都不同 . 新增的状态或行为一般与已有的不同 , 客户需要关注这些变化 .

6 计算机科学是一门相信所有问题都可以通过多一个间接层( indirection )来解决的科学

7. 间接层应用如此广泛,得益于它能带来如下好处:共享逻辑(重用)、分离意图和实现(提高灵活性)、 隔离变化(封装)、解耦等等。既然我们知道了间接层这么一回事,似乎我们可以不用知道设计模式也能做出像设计模式那样好的设计来。但是要记住,间接层应用过于泛滥,则会过犹不及,它会导致简单问题复杂化、跳跃阅读难以理解等问题

8. 做到设计模式的活学活用,我认为还要做到以解决问题为中心,将设计模式融合使用,避免为了设计而模式。当然这是建立在对各种设计模式了如指掌的情况下。比如,有一段解析字符串的对象,而在使用它之前,还要做一些参数的判断等其他非解析操作,这时,你很快就会想起使用代理模式;但是这个代理提供的接口可能有两三个(满足不同情形),而解析对象仅有一个接口,命名还不相同,这时你又想到了适配器模式。这时也许将两者融合成你独有的解决方案比笨拙的套用两个模式好的多得多。

9. 对象是什么?

从概念层面讲, 对象是某种拥有责任的抽象 。
从规格层面讲, 对象是一系列可以被其他对象使用的公共接口 。
从语言实现层面来看, 对象封装了代码和数据 。

10. 三大基本面向对象设计原则

针对接口编程,而不是针对实现编程
优先使用对象组合,而不是类继承
封装变化点

11. 理解了这些原则,再看设计模式,只是在具体问题上怎么实现这些原则而已。张无忌学太极拳,忘记了所有招式,打倒了 " 玄幂二老 " ,所谓 " 心中无招 " 。设计模式可谓招数,如果先学通了各种模式,又忘掉了所有模式而随心所欲,达到 " 心中无模式 ", 可谓 OO 之最高境界。

12. 我认为学习模式时,一定要理解一个模式的意图,结构,使用性。只有这样你才可能在实际的项目中使用,具体的实现时的细节可以翻阅资料,只有你非常了解模式时,才会很自然的应用模式

模式的意图,结构,优点和缺点 ( 使用性 )

13.Swing 中的 MVC

(JButton 举例子 )


Model:

一个按钮的 model 所应该具备的行为由一个接口 ButtonModel 来完成。一个按钮 model 实例封装了其内部的状态,并且定义了按钮的行为。它的所有方法可以分为四类:
1
、查询内部状态
2
、操作内部状态
3
、添加和删除事件监听器
4
、发生事件

View & Controller
上面的图中讲述一个按钮的 view/controller 由一个接口 ButtonUI 完成。如果一个类实现了这个接口,那么它将会负责创建一个用户界面,处理用户的操作。它的所有方法可以被分为三大类:
1
、绘制 Paint
2
、返回几何类型的信息
3
、处理 AWT 事件

程序员通常并不会直接和 model 以及 view/controller 打交道,他们通常隐藏于那些继承自 java.awt.Component 的组件里面了,这些组件就像胶水一样把 MVC 三者合三为一。也正是由于这些继承的组件对象,一个程序员可以很方便的混合使用 Swing 组件和 AWT 组件,然后,我们知道, Swing 组件有很多都是直接继承自相应的 AWT 组件,它能提供比 AWT 组件更加方便易用的功能,所以通常情况下,我们没有必要混合使用两者

示例 :JButton

顺序 JButton 源码 -----ButtonModel--------ButtonUI

14.OO 中划分责任,并将责任委派给不同的对象 . 可以加入中间层 .

Layers 架构模式

 在收集到用户对软件的要求之后,架构设计就开始了。架构设计一个主要的目的,就是把系统划分成为很多 " 板块 " 。划分的方式通常有两种,一种是 横向 的划分,一种是 纵向 划分。

  横向划分将系统按照商业目的划分。比如一个书店的管理系统可以划分成为进货、销售、库存管理、员工管理等等 。

 纵向划分则不同,它按照抽象层次的高低,将系统划分成 "" ,或叫 Layer 。比如一个公司的内网管理系统通常可以划分成为下面的几个 Layer:

 一、网页,也就是用户界面,负责显示数据、接受用户输入;

 二、领域层,包括 JavaBean 或者 COM 对象、 B2B 服务等,封装了必要的商业逻辑,负责根据商业逻辑决定显示什么数据、以及如何根据用户输入的数据进行计算;

 三、数据库,负责存储数据,按照查询要求提供所存储的数据。

 四、操作系统层,比如 Windows NT 或者 Solaris

 五、硬件层,比如 SUN E450 服务器等

 有人把这种 Layer 叫做 Tier ,但是 Tier 多带有物理含义,不同的 Tier 往往位于不同的计算机上,由网络连接起来,而 Layer 是纯粹逻辑的概念,与物理划分无关。

  Layers 架构模式的好处是:

 第一、任何一层的变化都可以很好地局限于这一层,而不会影响到其他各层。

 第二、更容易容纳新的技术和变化。 Layers 架构模式容许任何一层变更所使用的技术

创建模式是对类实例化过程的抽象。

一些系统在创建对象的时候需要动态的决定怎样创建对象、创建哪些对象、以及如何组合,表示这些对象。创建模式描述了怎样构造和封装这些动态的决定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值