Difference between revisions of "OOP Design Principles"
(→program to interfaces, not implementation) |
|||
(8 intermediate revisions by 2 users not shown) | |||
Line 3: | Line 3: | ||
Video from 4:28 until End | Video from 4:28 until End | ||
+ | |||
<youtube>https://www.youtube.com/watch?v=lahWUiYY-qc&index=6&list=PLCiOXwirraUAJPwNTW5gxfJK6Ej18fDP9</youtube> | <youtube>https://www.youtube.com/watch?v=lahWUiYY-qc&index=6&list=PLCiOXwirraUAJPwNTW5gxfJK6Ej18fDP9</youtube> | ||
Line 8: | Line 9: | ||
− | = | + | = Encapsulate What Varies = |
A test for good software design is how well it can deal with future change. Inevitably any piece of software that is in use will be asked to change to meet business needs has they evolve or as the problem space is better understood. | A test for good software design is how well it can deal with future change. Inevitably any piece of software that is in use will be asked to change to meet business needs has they evolve or as the problem space is better understood. | ||
Line 16: | Line 17: | ||
So, look for the parts most likely to change and prepare them for future expansion by shielding the rest of the program from that change. You should hide the potential variation behind an interface. Then, when the implementation changes, software written to the interface doesn’t need to change. This is called encapsulating variation. | So, look for the parts most likely to change and prepare them for future expansion by shielding the rest of the program from that change. You should hide the potential variation behind an interface. Then, when the implementation changes, software written to the interface doesn’t need to change. This is called encapsulating variation. | ||
− | + | = Favour Composition Over Inheritance = | |
− | |||
− | = | ||
Prefer composition over inheritance as it is more malleable / easy to modify later, but do not use a compose-always approach. With composition, it's easy to change behavior on the fly with Dependency Injection / Setters. Inheritance is more rigid as most languages do not allow you to derive from more than one type. So the goose is more or less cooked once you derive from TypeA. | Prefer composition over inheritance as it is more malleable / easy to modify later, but do not use a compose-always approach. With composition, it's easy to change behavior on the fly with Dependency Injection / Setters. Inheritance is more rigid as most languages do not allow you to derive from more than one type. So the goose is more or less cooked once you derive from TypeA. | ||
Line 26: | Line 25: | ||
Does TypeB want to expose the complete interface (all public methods no less) of TypeA such that TypeB can be used where TypeA is expected? | Does TypeB want to expose the complete interface (all public methods no less) of TypeA such that TypeB can be used where TypeA is expected? | ||
− | This indicates the need for Inheritance. | + | '''This indicates the need for Inheritance.''' |
− | e.g. A Cessna biplane will expose the complete interface of an airplane | + | e.g. A Cessna biplane will expose the complete interface of an airplane. Both are sufficiently related so it makes sense to derive from Airplane. |
===Test 2=== | ===Test 2=== | ||
Does TypeB want only some/part of the behavior exposed by TypeA? | Does TypeB want only some/part of the behavior exposed by TypeA? | ||
− | + | '''This indicates the need for Composition.''' | |
− | e.g. A Bird may need only the fly behavior of an Airplane. In this case | + | e.g. A Bird may need only the fly behavior of an Airplane. In this case not enough is sufficiently related so it makes sense to extract it out as an interface / class / both and make it a member of both classes (ie duplicate the code required in both). |
= program to interfaces, not implementation = | = program to interfaces, not implementation = | ||
+ | It means that you should try to write your code so it uses an abstraction (ie a simplified interface) instead of the implementation directly. So you should focus the design work on how users will interact with the code and you can then hide how the code is implementated. | ||
Worked example w.r.t. "finding commonality in otherwise unrelated items" | Worked example w.r.t. "finding commonality in otherwise unrelated items" | ||
http://stackoverflow.com/a/384067 | http://stackoverflow.com/a/384067 |
Latest revision as of 13:26, 24 August 2022
OOP has added many features and elements to basic Procedure-oriented programming. Writing good code, or designing OOP programs well, requires more care and planning. To help with writing good programs, OOP comes with some Design Principles to help us design and plan our programs.
Video from 4:28 until End
https://www.youtube.com/watch?v=lahWUiYY-qc&index=6&list=PLCiOXwirraUAJPwNTW5gxfJK6Ej18fDP9 (4:28 - End)
Encapsulate What Varies
A test for good software design is how well it can deal with future change. Inevitably any piece of software that is in use will be asked to change to meet business needs has they evolve or as the problem space is better understood.
A good design will allow for that change without too much work. A bad design will be very hard to modify.
So, look for the parts most likely to change and prepare them for future expansion by shielding the rest of the program from that change. You should hide the potential variation behind an interface. Then, when the implementation changes, software written to the interface doesn’t need to change. This is called encapsulating variation.
Favour Composition Over Inheritance
Prefer composition over inheritance as it is more malleable / easy to modify later, but do not use a compose-always approach. With composition, it's easy to change behavior on the fly with Dependency Injection / Setters. Inheritance is more rigid as most languages do not allow you to derive from more than one type. So the goose is more or less cooked once you derive from TypeA.
Test 1
Does TypeB want to expose the complete interface (all public methods no less) of TypeA such that TypeB can be used where TypeA is expected?
This indicates the need for Inheritance.
e.g. A Cessna biplane will expose the complete interface of an airplane. Both are sufficiently related so it makes sense to derive from Airplane.
Test 2
Does TypeB want only some/part of the behavior exposed by TypeA?
This indicates the need for Composition.
e.g. A Bird may need only the fly behavior of an Airplane. In this case not enough is sufficiently related so it makes sense to extract it out as an interface / class / both and make it a member of both classes (ie duplicate the code required in both).
program to interfaces, not implementation
It means that you should try to write your code so it uses an abstraction (ie a simplified interface) instead of the implementation directly. So you should focus the design work on how users will interact with the code and you can then hide how the code is implementated.
Worked example w.r.t. "finding commonality in otherwise unrelated items"