2 Spelling.
source | link

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 effectaffect every derived class, any change to the derived class will change it'sits 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 wholeywholly 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.

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 effect every derived class, any change to the derived class will change it's 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 wholey 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.

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.

1
source | link

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 effect every derived class, any change to the derived class will change it's 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 wholey 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.