880 likes | 1k Views
面向对象开发. 课程开发 :penghao 2008-04-01. 课程目的. 描述面向对象编程的一般性的手法,原则 和技巧 工欲善其事 , 必先利其器。. 课程大纲. 引言 面向对象的 表达方法 面向对象的 五个原则 面向对象的 几个设计模式. 我们面对的是什么?. 这种情况下的应对之道. 规模大且服务不断的情况下对应的非功能性需求就会要求比较多 . 变化多且持续变化的情况下要求程序具有良好的结构。本文档所关注的核心。. 程序,服务具有良好结构有两个层面的考虑.
E N D
面向对象开发 课程开发:penghao 2008-04-01
课程目的 • 描述面向对象编程的一般性的手法,原则 和技巧 • 工欲善其事,必先利其器。
课程大纲 • 引言 • 面向对象的表达方法 • 面向对象的五个原则 • 面向对象的几个设计模式
这种情况下的应对之道 • 规模大且服务不断的情况下对应的非功能性需求就会要求比较多. • 变化多且持续变化的情况下要求程序具有良好的结构。本文档所关注的核心。
程序,服务具有良好结构有两个层面的考虑 • 大粒度的结构设计。关注的层面比较宏观。这一块的知识参考 《面向模式的体系结构》 卷一。本文档不关注。
什么代码写得好?什么叫坏的代码?有标准吗?什么代码写得好?什么叫坏的代码?有标准吗? 2-5
坏代码举例 • 僵化:很难对系统进行改动,因为每个改动都会迫使许多对系统其他部分的改动。 • 脆弱:对系统的改动会导致系统综合改动的地方在概念上无关的许多地方出现问题。 • 牢固:很难解开系统的纠结,使之成为一些可重用的组件。 • 粘滞:做正确的事情比做错误的事情要困难。 • 不必要的复杂:设计中包含不具有任何直接好处的基础结构。 • 不必要的重复: • 晦涩:很难阅读,理解。 • (引用于《敏捷软件开发:原则,模式与实践》 强烈推荐该书。)
好代码的近似标准介绍 • OO(或者说语言)的核心思想 • OO的五大技术原则(标准) • 从OO看合理的分层架构
OO(或者说语言)的核心思想—表达领域概念 • 语言的语法都是比较简单的。不过同样的语法写出来的代码却有好坏高低之分。 • 重要的区别就在于表达领域概念的水平 • 重要的原则就是:“程序(代码)应该反映人的思想,而不是相反。”Martin fowler.(《分析模式》)
通过对领域概念应用OO原则,分析模式,设计模式等等手段对对象职责进行抽象,细分职责。形成最终静态对象图。通过对领域概念应用OO原则,分析模式,设计模式等等手段对对象职责进行抽象,细分职责。形成最终静态对象图。 • 通过对用例画时序图,确认对象职责是否可行,恰当。
表达概念的几种类型 • 释意接口。 • 表达实体对象 • 表达约束 • 表达流程(服务) • 表达规格 • 隐含机制表达
“在初学面向对象时对它们并不熟悉。每学会一个这样的表达的类型,我的设计水平就又进了一步”“My designs became sharper with each one of these I leaned.” • Eric Evans 《领域驱动设计》强烈推荐该书
释意接口的例子 • int CPlayer::StandUp( int iSessionID ) • { • int iOpenNum = 0; • iOpenNum = m_Sessions.GetLastIdx(); • //下面两个判断判断违法情况。 • if( iSessionID < 0) • { • } else if(iSessionID > iOpenNum) { } • }
推荐写法如下: • RoomSession 提取成一个对象(类),提供CheckRoomIDValid接口。 • int CPlayer::StandUp( int iSessionID ) • { iRet = m_Session.CheckValid(iSessionID); If(iRet) { } • }
这种是完全达到了隐藏实现,消除了代码重复,又充分表意的目的。特别在此强调释意接口。这种是完全达到了隐藏实现,消除了代码重复,又充分表意的目的。特别在此强调释意接口。 • 好的类 应该是每个函数都比较短,五,六十行。然后有二,三十个方法。这个类形成一个接近封闭的类型。具有充分的自我表达能力。
实体表达的例子 • char sNowDate[20]={0}; • char sLastDate[20]={0}; • GetDate(time(NULL),sNowDate); • GetDate(iReserve[LastGive], sLastDate); • if(strcmp(sNowDate, sLastDate) > 0) • { • } • else • { • }
同一个功能的另外一种写法 也是实际代码。 • int CGiveDaily::OnGive(CAttach *pPA, int iHE) • { • Date dToday(time(NULL)); • Date dLastDay(pPA->GetGiveHELastTime()); • if(dLastDay == dToday) • { • } • else • { • } • }
可以看出 抽取出了 日期类,用日期类 来表达人的思想,而不是只记得使用哪个API来实现功能。 • 日期这个概念在这个需求里面是比较重要的一个概念。面向对象就表达出了这个概念。
约束表达的例子 • 有时候某些约束表达的是领域逻辑。通常隐式的表达,将他们显示表达出来能够极大地改进设计。 • 比如QQGame 里面进入房间限制。
实际产品对进房需求的演化 • 第一个版本:限制一下玩家最低,最高积分。 • 第二个版本超级玩家和网管要优先进房 • 第三个版本如果是出租房间需要是承租方才能进房 • 第四个版本如果是比赛房间需要是比赛方才能进房 • 第五个版本需要拥有某种道具 • 第六个版本如果是欢乐场房间需要检查欢乐豆上下限制。
产品的需求演化说明进房约束是个热点问题。 • 抽象出约束接口,把约束的增加,约束的实现,变更对进房主流程屏蔽起来。
基本行为流程概念的表达,对于产品同事不断提及到的流程概念表达。基本行为流程概念的表达,对于产品同事不断提及到的流程概念表达。
把规格用对象来表达 • 验证。 • 选择(查询) • 按需创建
隐含概念表达实例 • 在QQGame里面对房间模式的描述结构如下。不同的房间模式,具有不一样的基本行为。 • 我们通过不同的工厂实现来屏蔽掉这一层变化。不同的房间聚合不同的生成工厂。 • 房间模式是挖掘出来的概念,抽象工厂表达出了这个概念。
OO的五项技术原则 • 单一职责—事情不要太复杂。 • 开闭原则—让易变的容易变化,不变的稳定。 • 依赖倒置—让易变的依赖于稳定,而不是相反。 • 接口隔离 • 可替代
OCP(Open-Closed Principle) : 开放-关闭原则 对扩展要说YES,对修改说NO。 LSP(Liskov Substitution Principle) : LSP原则 父母要对孩子负责。 SRP(Single Responsibility Principle) : 单一职责原则 感情要单一,不要脚踏两支船。 DIP(Dependency Inversion Principle) : 关系返转原则 不要一天打10个电话给我,有结果我会通知你。 ISP(Interface Segregation Principle) : 接口隔离原则 让李部长只负责开发吧,市场就让辛部长做吧
学习面向对象中最主要的一点就是,理解面向对象原则。如果你还没有真正理解这些原则,那你做出的设计可能不是最好的设计,那你学习设计模式也不能真正理解它的真谛。学习面向对象中最主要的一点就是,理解面向对象原则。如果你还没有真正理解这些原则,那你做出的设计可能不是最好的设计,那你学习设计模式也不能真正理解它的真谛。
五项技术原则实例-单一职责 • 实际代码例子 • class CDBCtrl • { • public: • CDBCtrl(); • ~CDBCtrl(); • int Initialize(const char *szCfgFile); • int PrepareToRun(); • int Run(); • void* operator new(size_t nSize); • void operator delete( void* pMem); • static int CountSize(); • static CSharedMem *pCurrentShm; • private: • int ReadCfg(const char *szCfgFile); • int LoadProxyCfgFromFile(char *szProxyFile, int& iProxyNumber); • int LoadGivenRuleFromFile(char *szRuleFile, int& iRuleCount); • int LoadForbidRuleFromFile(char *szRuleFile, int& iRuleCount); • int ReadPublicCfg(const char *szCfgFile); • int LoadCreditConfig(const char *szCfgFile); • int CheckRunFlags(); • int ConnectToProxys(); • int CheckAndDispatchInputMsg(); • int RoutineCheck(); • int DispatchOneCode(short nCodeLength, BYTE* pbyCode); • int PostInternalMsgToHandle(int iHandleID, CMsg *pMsg); • int NofityDBHandles(CMsg *stMsg) ; • TDBCfg m_stDBCfg; • CTCPConn m_astProxyConn[MAXPROXYNUMBER]; • CDBHandle* m_apHandles[MAXHANDLENUMBER]; • int m_iUdpSoket ; • CASqlExe* m_apExecers[MAXHANDLENUMBER]; • CExceptionTab m_stExceptionTable; • };
class CMainCtrl • { • public: • CMainCtrl(); • virtual ~CMainCtrl(); • virtual int Initialize(); • virtual int Run(); • int ReloadCfg(); • int CheckRunFlag(); • protected: • private: • };
职责表达了负责的变化,有多个维度的职责,就有多个维度的变化职责表达了负责的变化,有多个维度的职责,就有多个维度的变化
五项技术原则实例-开闭原则 • QQGame 最经典的例子,Main Frame可以不动代码,游戏服务可以源源不断输出新的游戏。觉得这个是最核心的规则。(下图引用自zengyu《QQGame服务器架构技术》)
五项技术原则实例-依赖倒置 • 问题界面控件,比如Button,ListBox,等。它们都是想做成通用的,也就是说和业务逻辑不相关。但是事件响应 是通过界面控件传递到逻辑的。 • 问怎么做到界面控件和业务逻辑隔离?
经典的解决办法(参考QQGame大厅设计以及 《敏捷软件开发》)