Why should implementation of IUnknown split into several templates in ATL namely CComObjectRootEx<> and CComCoClass, when there is no complexity in implementing IUnknown?
Why should STL have an abstract model of containers, iterators and alogirthms?
http://www.stroustrup.com/Programming/20_containers.ppt
Why should Enterprise library have a complex abstract object model?
http://msdn.microsoft.com/en-us/library/ff648712.aspx
"Encapsulate what Varies" Principle in ATL COM (Using Templates)
Object Model

CComXxxThreadModel encapsulates thread safe increment and decrement operation on life-time counter.
Why should STL have an abstract model of containers, iterators and alogirthms?
http://www.stroustrup.com/Programming/20_containers.ppt
Why should Enterprise library have a complex abstract object model?
http://msdn.microsoft.com/en-us/library/ff648712.aspx
Major goals as mentioned in their respective documents : Flexibility , Extensibility , Separation of Concerns , greater possibility of reuse , taking advantage of tested, stabilized and proven libraries and frameworks.
In Agile mode of development, we cannot come up with an abstract model/design for the problem solution(containing multiple business scenarios) we are solving upfront, since we design and implement scenario by scenario. Over doing generalization up-front without proper understanding of concrete implementation, creates complexity and might make the code unmanageable.
Its a challenge to identify when to stop generalization for a problem.
Its a challenge to identify when to stop generalization for a problem.
Initially while coming up with Subsystem, identifying the functionality that might vary should be abstracted . Design patterns for abstracting varying functionality and making that code swappable and independently manageable without ripple effects needs to be identified (Strategy, Template patterns, plug-gable framework).
Any pattern implementation satisfies the following principles along with catering to its basic intent.
Any pattern implementation satisfies the following principles along with catering to its basic intent.
- Code to an interface
- Encapsulate What Varies
- Only One Reason to Change
- Classes are about behavior
- Prefer delegation over inheritance
- Dependency Inversion Principle
And after having concrete implementations for couple of scenarios, during refactoring again identifying the common functionality that's required for all scenarios and abstracting that functionality into separate classes needs to be done.
"Encapsulate what Varies" Principle in ATL COM (Using Templates)
Object Model

CComXxxThreadModel encapsulates thread safe increment and decrement operation on life-time counter.
class CComSingleThreadModel
{
static ULONG WINAPI Increment(LPLONG p) { return ++(*p); }
static ULONG WINAPI Decrement(LPLONG p) { return (*p); }
...
};
class CComMultiThreadModel
{
static ULONG WINAPI Increment(LPLONG p) { return InterlockedIncrement(p); }
static ULONG WINAPI Decrement(LPLONG p) { return InterlockedDecrement(p); }
...
};
And in the implementation of CComObjectRootEx, ThreadModel is passed as class parameter:
template <class ThreadModel> class CComObjectRootEx : public CComObjectRootBase
{ public: typedef ThreadModel _ThreadModel; typedef typename _ThreadModel::AutoCriticalSection _CritSec; typedef typename _ThreadModel::AutoDeleteCriticalSection _AutoDelCritSec; typedef CComObjectLockT<_ThreadModel> ObjectLock;
.....
};
The static methods in ThreadModel variations, will be used in internal AddRef and Release:-ULONG InternalAddRef()
{ ATLASSERT(m_dwRef != -1L); return _ThreadModel::Increment(&m_dwRef); } ULONG InternalRelease()
{ #ifdef _DEBUG long nRef = _ThreadModel::Decrement(&m_dwRef); if (nRef < -(LONG_MAX / 2))
{ ATLASSERT(0 && _T("Release called on a pointer " "that has already been released")); } return nRef; #else return _ThreadModel::Decrement(&m_dwRef); #endif }
We might not always have a value add in designing a reusable Framework that encapsulates separately what varies
and core that would be constant .
To reduce the duplication of boiler plate code, to keep it easily maintainable and extensible we might need to refactor code to ensure
above principles are taken care of.
Consider
Repository pattern implementation in Domain Driven Design (using C#). Repository
is generally used to search a domain object(Root Entities or Entities) in an Aggregate. Repository also
provides Persistence Ignorance that abstracts the physical storage code.
public interface IRepository<T> where T : class
{
T GetById(int id);
IEnumerable<T> GetAll();
IEnumerable<T> Query(Expression<Func<T, bool>> filter);
void Add(T entity);
void Remove(T entity);
}
In the above interface,
T represents any domain object specific to our domain(Account,
AccountTransaction etc).
An abstract Repository
class , implements common operations for all Domain Aggregates:-
public abstract class Repository<T> : IRepository<T> where T : class
- public IEnumerable<T> GetAll()
- public abstract T GetById(int id);
- public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
- public void Add(T entity)
- public void Remove(T entity)
And each specific
repository for specific Aggregate root (Ex:- ShoppingCart) in the Bounded
context of (Ex:- Billing) inherits from the abstract base class Repository and
provide specific implementation for GetById and any other specific methods for
Validation etc:-
public partial class ShoppingCartRepository : Repository<ShoppingCart>
No comments:
Post a Comment