Embedded Object Oriented Design Tips II

来源:百度文库 编辑:神马文学网 时间:2024/05/01 17:12:46
Object Oriented Design Tips II
We have already covered object oriented design tips in a previous article.Here we will look at more tips that will help you improve your object orienteddesign skills:
Class with just get-set methods points to missed delegation
Replace an array of structures with an array of objects
Delegate work to helper class
Multi-dimensional arrays point to incomplete class identification
Multiple nested loops point to incomplete delegation
Class with very large numbers of methods points to incomplete class identification
Don't go overboard with inheritance
Prefer delegation to inheritance
Don't scatter the abstraction
Consider group of objects to split work amongst team members
Use nested classes for lightweight helper classes
Use templates to improve type safety and performance
Divide your code into framework and application parts
Many times while developing classes you might find that a particular classyou have developed has just get and set based methods. There are no methods toperform any operations on the object. In many cases, this points toinadequate delegation of work by the caller. Examine the caller of the get-set methods. Lookfor operations that could be delegated to the class with just get-setmethods.
The example below shows a DSP class that has get and set methods. The MessageHandler class was doing most of the processing.
Overworked Message Handler class and a simple Get-Set DSP class
class DSP { public: Queue *GetWriteBufferQueue(); Queue *GetReadBufferQueue(); void SetStatusRegister(int mask); void SetCongestionLevel(); void SetWriteBufferQueue(Buffer *pQueue); void SetReadBufferQueue(Buffer *pQueue); }; Status MessageHandler::SendMessage(Buffer *pBuffer) { int dspId = pBuffer->GetDSP(); DSP* pDSP = m_dsp[dspId]; Queue *pQueue = pDSP->GetWriteBufferQueue(); int status = pQueue->Add(pBuffer); pDSP->SetStatusRegister(WRITTEN_MESSAGE); if (pQueue->GetLength() > CONGESTION_THRESHOLD) { pDSP->SetCongestionLevel(); } return status; }
The above classes have been transformed to assign most of the DSP queuemanagement to the DSP class itself. This has simplified the design of theMessage Handler class. The interfaces of the DSP class have also beensimplified.
Here the DSP class does most of the work
class DSP { public: void WriteBuffer(Buffer *pBuf); Buffer *ReadBuffer(); }; Status MessageHandler::SendMessage(Buffer *) { int dspId = pBuffer->GetDSP(); pDSP = m_dsp[dspId]; int status = pDSP->WriteBuffer(); return status; } Status DSP::WriteBuffer(Buffer *) { int status = m_pQueue->Add(pBuffer); IOWrite(WRITTEN_MESSAGE); if (m_pQueue->GetLength() > CONGESTION_THRESHOLD) { m_bCongestionFlag = true; } return status; }
Whenever you end up with an array of structures in your class, consider ifyou should convert the array of structure into an array of objects. Initially thestructure array might be simple with only one or two fields. As coding progresses,more and more fields are added to the structure. At that time it might be toolate to treat the structure as a class.
If you find that one of the classes in your design has too many methods andthe code size for the class is much greater than your average class, considerinventing helper classes to handle some of the functionality of this class. Thiswill simplify the design of the huge class, making it more maintainable. Moreimportantly, you might be able to split the work amongst different developers.
Consider the following class:
Monolithic Digital Trunk Class
class DigitTrunk { Status m_status; Timeslot m_timeslot[MAX_TIMESLOT_PER_TRUNK]; int m_signalingTimeslot; int m_signalingStatus; . . . int m_errorThreshold; int m_localErrorRate; int m_remoteErrorRate; . . . public: . . . void HandleSignalingRequest(); void SendSignalingIndication(); . . . void HandleRemoteError(); void HandleLocalError(); . . . };
The above class can be made more maintainable by adding private helper classesSignalingHandler and ErrorHandler.
Digital Trunk Class With Helper Classes
class DigitTrunk { Status m_status; Timeslot m_timeslot[MAX_TIMESLOTS_PER_TRUNK]; class SignalingHandler { int m_signalingTimeslot; int m_signalingStatus; . . . public: void HandleSignalingRequest(); void SendSignalingIndication(); }; class ErrorHandler { int m_errorThreshold; int m_localErrorRate; int m_remoteErrorRate; . . . public: void HandleRemoteError(); void HandleLocalError(); }; // Helper classes SignalingHandler m_signalingHandler; ErrorHandler m_errorHandler; public: . . . void HandleSignalingRequest() { m_sigalingHandler.HandleSignalingRequest(); } void SendSignalingIndication() { m_signalingHandler.SendSignalingIndication(); } . . . void HandleRemoteError() { m_errorHandler.HandleRemoteError(); } void HandleLocalError() { m_errorHandler.HandleLocalError(); } . . . };
If your design contains multi-dimensional arrays, this might point to missedclass identification. The following example should clarify this:
Two dimensional DSP array declaration
. . . // Each Signal Processor Card contains 32 DSPs. // This is represented by a two dimensional // array of DSP objects. The first dimension // is the Signal Processor Card Id and the second // dimension is the DSP id on the card DSP m_dsp[MAX_SIGNAL_PROCESSOR_CARDS][MAX_DSPS_PER_CARD];
The above two dimensional array points to missed identification ofSignalProcessingCard class. This has been fixed in the following codefragment:
SignalProcessingCard class eliminates two dimensional array
. . . // The two dimensional array is replaced. We identify // SignalProcesingCard as an object. This // object contains 32 DSPs class SignalProcessingCard { DSP m_dsp[MAX_DSPS_PER_CARD]; public: . . . }; // Array of signal processing card objects, indexed by signal processing card id SignalProcessingCard m_signalProcessingCard[MAX_SIGNAL_PROCESSOR_CARDS]; . . .
Many times, nested loops point to incomplete delegation. May be the innernesting of the loop should have been delegated to a lower level object. Considerthe above example of SignalProcessingCard and DSP.
Initializing all DSPs (Nested Loops)
. . . for (card=0; card < MAX_SIGNAL_PROCESSING_CARDS; card++) { for (dsp=0; dsp < MAX_DSPS_PER_CARD; dsp++) { m_signalProcessingCard[card].GetDSP(dsp)->Initialize(); } }
The inner loop in the above code should be replaced with a Initialize methodat SignalProcessingCard. Code operating on SignalProcesingCard initializationshould not worry about DSP level initialization. This should be delegated to theInitialize method of the SignalProcessingCard.
Initializing all DSPs delegated to SignalProcessingCard class (no nested loop)
. . . for (card=0; card < MAX_SIGNAL_PROCESSING_CARDS; card++) { m_signalProcessingCard[card].Initialize(); } void SignalProcessingCard::Initialize() { for (dsp=0; dsp < MAX_DSPS_PER_CARD; dsp++) { m_dsp[dsp].Initialize(); } }
A class with very large number of methods typically means that fine grainobject identification has been missed. At this stage, have a hard look at yourdesign to identify more classes.
This is a very common mistake made by designers new to object orienteddesign. Inheritance is such a wonderful concept that its easy to go overboardand try to apply it every where. This problem can be avoided by using the litmustest:
X should inherit from Y only if you can say that X is a Y. By this ruleits easy to see that Circle should inherit from Shape as we can make thestatement "Circle is a Shape".
Inheritance is the most tightly coupled of all the relationships. Everyinheritance relationship causes the derived class to strongly depend upon thebase class. That dependency is hard to manage.
Also note that the biggest benefit of object oriented design are obtainedfrom composition and not inheritance. In our earlier example, programmers can developSignalProcessingCard and DSP objects as if there was only one instance of theobject. The multiplicity is achieved by just declaring an array of the objects.
Many times, relationships are better modeled as delegation thaninheritance. When in doubt, always consider delegation as an alternative.Sometimes commonality in classes that do not meet the "is a" rule isbetter implemented by using a common helper class which implements the commonfunctionality. This class can then be included as a member in both the classes.
Consider two classes TerminalAllocator and DSPAllocatorwhich use similar resource allocation algorithms. The two classes havecompletely different type of interfaces. You might be tempted to model this asboth the classes inheriting from a common Allocator class which implements thecommon parts of the allocation algorithm. In many cases, it might be better tomodel TerminalAllocator and DSPAllocator as standalone classes with a helperclass Allocator included as a member.
Modeled as Inheritiance
class TerminalAllocator : public Allocator { . . . }; class DSPAllocator : public Allocator { . . . };
Modeled as Delegation
class TerminalAllocator { Allocator m_allocator; . . . }; class DSPAllocator { Allocator m_allocator; . . . };
This is a common mistake when multiple developers are working on a project.Each developer implements his or her part by designing objects that they need,without considering if other developers have similar objects. This scatters theabstraction of an object into several different objects which implement piecesof the whole objects functionality. In our example, this would mean that thedesign contains several objects that represent the SignalProcessingCard and DSPobjects in different portions of the code. Each developer implement parts of theSignalProcessingCard and DSPfunctionality that is needed in their domain. This results in scattering thefunctionality of an object over several incomplete objects.
Needless to say, such code would bedifficult to understand and hard to maintain.
Embedded software developers often split work amongst team members bydividing the functionality into several tasks. With object oriented design, workcan be divided in a much more fine grain way by assigning a group of classes to adeveloper.  In many cases you can implement all the functionality in asingle task, thus greatly reducing the effort in designing intra-processorcommunication.
Many times you will encounter a situation where a small class might be usefulin capturing some of functionality of a large class. Often developers avoidadding such classes as they would result in a new set of header and sourcefiles. This brings its associated changes like makefile updates, checking innew elements. Another problem with the this approach is that you end up withsimply too many classes. There is no way to isolate the important classes fromthe simple helper classes.
The solution to this problem is to use small nested classes that are declaredwithin the parent class. With this approach, the nested class does not appearamongst the top level classes in your design. This greatly simplifies the totalnumber of high level classes you have to deal with. (If you are using a toollike Microsoft Visual Studio, the nested classes would appear as tree nodesinside the parent class. Thus adding a new class does not increase the number ofclasses visible in the outermost nodes of the tree).
Nested classes can be made even more lightweight by lettingdevelopers write the code for the nested classes in the parent classsource files. Thislightweight mechanism would improve the readability of complex classes.Thedevelopers can now model the complex class as a set of lightweighthelperclasses.
DigitalTrunk.h : Nested class declaration
class DigitalTrunk { private: // Timeslot allocator is a private class used by DigitalTrunk. The full // name for the class is DigitalTrunk::TimeslotAllocator. class TimeslotAllocator { public: int Allocate(); void Free(int timeslot); } // Timeslots in transmit and receive direction can be allocated // independently TimeslotAllocator m_transmitAllocator; TimeslotAllocator m_receiveAllocator; public: . . . };
DigitalTrunk.cpp : Nested class methods
// Nested classes methods are also contained // in the the parent class's CPP file int DigitalTrunk::TimeslotAllocator::Allocate() { } void DigitalTrunk::TimeslotAllocator::Free() { }
Do not restrict yourself to using templates as defined in STL. Templates canbe used to provide type safe and efficient code in the following cases:
Classes have common functionality but differ in the size of data structures. Such classes can be modeled in base template class that takes the data structure sizes as template parameters.
Preprocessor macros are type independent but type unsafe. C++ inline functions are type safe but type dependent. Template functions can be used to replace macros as well as regular inline functions. Template functions are both type safe and type independent.
Pointer and reference based classes where the functionality is the same in classes but the type to operate on is different. In most such cases declaring template base class with a generic type would solve this problem in an elegant fashion.
When developing a new application consider dividing the total application intocore application code and framework code. The core application code performsoperations that are very specific to the application at hand. All the other codethat is needed to support the core application should be modeled as anapplication framework. This has several benefits:
The application framework developed here might get reused in developing similar applications. The application framework can be reused much more readily than the core application.
Lower layers of the application framework might be reused in applications that are quite different from the original core application.
The core application can to be ported to a different platform by just changing the application framework.
Often developing the core application and framework requires different skills. This application- framework can simplify staffing the project.
Here are a few examples of possible frameworks:
Tracing framework
Memory management framework
Message management framework
Call processing framework
Operator interface management framework
Fault handling framework
Embedded Object Oriented Design Tips II Embedded Object Oriented Design Tips Liskov Substitution Principle of Object Oriented Design STL Design Patterns in Embedded Systems II Object-Oriented Programming with JavaScript, Part II: Methods Object Oriented Programming in C 石头札记: CSS Oriented Design Issues in Embedded and Realtime System Design Publish-Subscriber Design Patterns in Embedded Systems 面向对象分析方法(Object-Oriented Analysis,OOA) [object] RTPEG-32 - Portable Embedded GUI for Embedded... Windows Embedded 简介 object - HTML元素 什么是object指向? 什么是object指向? [object]北京故宫博物院 HTML 标签 [Bernstein09] 10.5. Service-Oriented Architecture Interviewing Tips Resume Tips 生活tips 20 tips 美美TIPS