5

I know what SRP is, but am questioning my current design of an object I call an entity. Below is a picture of the design I am referring to. If I shift the code from the GeometricInformation and TransactionInformation objects to the abstracts GeometricEntityBase and TranslactionalEntityBase respectively, would this be considered a severe breech of SRP? I'm hoping the experts here can help poke holes in this design or affirm it if appropriate.

To summarize the intent, I have a CAD API I'm working on. Each entity in the CAD realm is represented by an Entity in code. To allow Entities to only carry those attributes appropriate to its purpose there's three levels of inheritance the Entity can use. For example, if only the very basic Entity features (like having an ID, etc) then it can just inherit from EntityBase. If it requires additional geometric properties to identify boundaries, insert point, transformation matrix, etc, then it would inherit from GeometricEntityBase. If it is to be a fully transacted entity (via a custom transactional framework), then it is required to carry the attributes of both plus transactional (such as the entity state (new, clean, dirty), document, etc).

To ensure SRP, I used the adapter pattern and set the geometric attributes and operations to an object called GeometricInformation and likewise the transactional stuff to TransactionInformation. This is working great, but I am now beginning to realize that in order to allow changes to geometric properties, I need to validate against the entity state. In otherwords, the GeometricInformation object has to know about the attributes of the TransactionInformation object. Solutions seem to involve an observer pattern such that the GeomtetricInformation could observe the TransactionInformation (which seems forceful) or assert an XXXEntityBase property "CanModify" that can be changed and observed by either XXXInformation object. Unfortunately this would create a two-way dependency since each XXXInformation object would have to know it's parent.

Because of the additional fact that WCF is used to communicate the entities to a lower-level data layer, there's also a translation process to convert these to DTO equivalents. In the end, I'm thinking of moving the code from the XXXInformation objects to their abstract equivalents which has very little in them anyways, but am concerned that breaking SRP would potentially cause downstream issues too. On the otherhand, this would streamline the entities more and allow the translation process to improve in terms of performance too so definitely some give-and-take.

enter image description here

  • 1
    your abstract base classes probably serve no purpose, and you don't what MyEntity to inherit functionality from them, you want it to use functionality from them, so implement them independently and it can use geometric information, and transaction information together the way it needs to. – Jimmy Hoffa Feb 1 '16 at 19:53
  • Do you have a specific question? – Robert Harvey Feb 1 '16 at 19:55
  • @RobertHarvey: Yes, my question is specific to the scenario in the image shown per my design intent. My problem is (as Jimmy Hoffa has acknowledged) is with respect to dependencies in this scenario resulting from the chain of inheritance while shifting code from these objects would otherwise break SRP. If you need a specific question, then "How do I more effectively design this per its intended design while ensuring I'm meeting SOLID design principles?" – bjhuffine Feb 1 '16 at 19:59
  • @RobertHarvey: I hope that sounds okay. I'm not trying to be sarcastic, I'm just not sure how to phrase the question exactly. – bjhuffine Feb 1 '16 at 20:15
  • Wall of text. Some formatting would help better convey the key ideas of such a complex question. – Tulains Córdova Feb 1 '16 at 21:56
5

This is precisely why inheritance should never be the default choice for creating reusable features / functionality.

Your issue boils down to you have an inheritance hierarchy that may in certain scenarios need knowledge across parts of the inheritance graph, making the graph have even more relationships between each node in it. These relationships are between types and are a form of coupling. One that is best avoided by simply not involving inheritance for a vast majority of scenarios.

Look at it this way, when using inheritance you end up stacking dependencies on top of each other such that they cannot be changed independently. Any change to the base class will affect every derived class, any change to the derived class will change its relationship to the base class and every other class deriving from it. Your situation now is that you want to make two derived classes have facilities the base class doesn't, but it doesn't make sense to promote that because it's specialized to those two classes.

This is where composition comes in. Instead of making an inheritance hierarchy of things which are alike, it's often better to approach it from the standpoint that you have pieces of functionality you want. By isolating units into pieces of functionality you can pick and choose which pieces to use and how.

In your example for instance, you want transactional behaviours and you want boundary validation. Those should likely be two wholly independent not-related units. Then when you have a class that wants transaction behaviour, it pulls in a transactional class/module/what-have-you as a member field or property. If it wants boundary validation also, it pulls in a boundary-validation unit. Then it can compose the two by passing information back and forth between transactional activities and boundary validation.


Another very large flaw you're falling into here appears to be a strong adherence to design patterns as solutions. They aren't solutions because they don't solve problems, they simply give conceptual ideas about how you might meet some set of design characteristics. The reality is you want to fulfill a set of good design characteristics (like SRP, and the rest of SOLID for instance), but you want to do so in a tailor-made solution to your problem domain. Don't reach for an "observer", just try and fundamentally design an ideal solution to the problem of separating concerns that you have, in a way which meets as many positive design characteristics as you can. If you find that a design pattern is the result then so be it, but don't start from the standpoint of using one.

Always start solving a problem by breaking it down into sub-problems until you have easy ones to solve. Then when you reintegrate those solutions into a cohesive whole, you want those integrations to be done in a way that meets SOLID design principles as much as practicable, as well as any other design and implementation characteristics important given the demands of your solution. Perhaps it needs to run fast and so you give up on integrating the solutions together abstractly/cleanly as much as you'd like for instance, perhaps it needs reliability so you add a great deal more. The patterns are just conceptual ideas, not to be followed to the letter or taken as literal solutions.

  • Okay, so I believe I get where you're going with this. With composition I could define one base class (since I do need some basic attributes/operations for any inheriting entity... this is the baseline for domain development) and it's constructor could say accept types implementing interfaces for objects pertaining to the features required at that level. Even so, let's say there's Transactional and Geometric objects to pass in to add this functionality and further let's say that Geometric requires knowledge from Transactional. How would you address that? – bjhuffine Feb 1 '16 at 20:12
  • 1
    @bjhuffine with composition you could create one Entity class with those pieces of information and you wouldn't need to make other classes inherit from it at all. They could all take that class and utilize it to describe those properties/attributes of themselves. Like in a Building class, instead of inheriting from Business you could have an instance of Business in the building class which describes the business in that building. All the things that Building wants to do with it's Business like CauseInsuranceClaim() it could do through calls directly to it's Business – Jimmy Hoffa Feb 1 '16 at 20:16
  • @bjhuffine if inheritance absolutely makes sense for what you're doing then go ahead and use it, but look for the option of composition first because it allows a greater flexibility and looser coupling than inheritance. It's typically always preferable to compose pieces of functionality rather than derive them for this reason. – Jimmy Hoffa Feb 1 '16 at 20:18
  • Okay, I gotcha. Thank you very much. I hadn't thought of composition vs inheritance. Thanks also for your wisdom on implementation as well. – bjhuffine Feb 1 '16 at 20:23
  • 1
    The GeometricEntityBase and the TransactionalEntityBase objects are now independent from each other, minus the common EntityBase, and therefore freed me up much more. Using the compositional approach, I am better able to leverage the Dependency Inversion principles too which will make a huge difference too. Thanks for your help! – bjhuffine Feb 11 '16 at 20:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.