Principles
Interface Segregation Principle (ISP)
Why?
Service descriptions that are independent of a specific fulfillment are independent.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
The Interface Segregation Principle (
ISP) is another
SOLID Principle.
Segregation means
Separation. The principle states that a client should not be dependent on details of a service that it does not need. The less is contained in its interface, the lower the coupling between the two components.
Let's imagine that we have to plan a connector to connect a monitor to a computer. We decide to simply make all the signals that occur in a computer available via a connector. It may have a few hundred pins, but it is extremely flexible. Unfortunately, this also maximizes the coupling.
In the example of the connector, it is obvious that a monitor connection should only contain those signals that are required to display an image on the monitor. The same applies to software interfaces. They should also be as small as possible to avoid unnecessary coupling. And just like the monitor connector, the interface should have a high level of cohesion: It should only contain things that really belong closely together.
To apply the interface segregation principle, the two refactorings
Extract Interface and
Extract Superclass available.
Sources
Source |
Author |
Brief description |
ObjectMentor |
Robert C. Martin |
Article on the Interface Segregation Principle from 1996, published in the Engineering Notebook for The C++ Report |
Dependency Inversion Principle (DIP)
Why?
Precise testing requires the isolation of classes. Isolation occurs when classes no longer contain any dependencies on implementations - neither at runtime nor at translation time. Concrete dependencies should therefore be decided as late as possible. Preferably at runtime.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
The Dependency Inversion Principle (
DIP) is a
SOLID principle. It says the following:
- High-level classes should not be dependent on low-level classes, but both should be dependent on interfaces.
- Interfaces should not be dependent on details, but details of interfaces.
If a high-level class uses a low-level class directly, there is a strong coupling between the two. At the latest when trying to test the high-level class in isolation, you will encounter difficulties. For this reason, the high-level class should be dependent on an interface, which in turn is implemented by the low-level class. In this way, the low-level class in the unit test can be implemented by a
Mockup be replaced.
In principle, there are three options for resolving the inverted, abstract dependency with a concrete object at runtime:
- using constructor parameters "by hand"
- Use of an inversion of control container (IoC container) such as Castle Windsor
- Dependency Lookup
In the
yellow degree we initially only inject the dependencies via the parameters of the constructors. Initially, this is the simplest solution and works quite well with a handful of classes. Later in the
green degree we use an IoC container and dependency lookup.
Sources
Source |
Author |
Brief description |
objectmentor |
Robert C. Martin |
Article on the Dependency Inversion Principle from 1996, published in the Engineering Notebook for The C++ Report |
Liskov Substitution Principle (LSP)
Why?
Anyone who has to deal with heirs does not want any surprises when they are familiar with testators.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
The Liskov Substitution Principle (
LSP) is a
SOLID principle. It states that subtypes must behave in the same way as their base type. This sounds banal at first. The example of exceptions makes it clear what problems arise when the principle is violated: If the base type does not throw an exception when executing a method, all subtypes must adhere to this rule. If the method of a subtype nevertheless throws an exception, this would cause problems for users who expect an object of the base type because they are not prepared for this. If the base type does not throw an exception at this point, the user is not prepared to have to handle exceptions.
In more general terms, the principle can also be expressed in such a way that a subtype may only extend the functionality of a base type, but not restrict it. If a method in the base type is defined on a certain value range, the subtype may adopt or extend this value range, but it may not restrict it under any circumstances.
The Liskov Substitution Principle also recommends thinking very carefully about inheritance. In the vast majority of cases, composition is preferable to inheritance (
Favor Composition over Inheritance). With inheritance, you should always think about the behavior, not just the structure. Instead of inheritance as
is-a relation and only consider the (data) structure, it is better to speak of a
behaves-as relation and take into account the behavior of the class.
Sources
Source |
Author |
Brief description |
objectmentor |
Robert C. Martin |
Article on the Liskov Substitution Principle from 1996, published in the Engineering Notebook for The C++ Report |
Principle of Least Astonishment
Why?
If a component unexpectedly behaves differently than expected, its use becomes unnecessarily complicated and error-prone.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
Software development is to a large extent a creative process. In this process, it is important to immerse yourself in the flow. Once you have reached this state, the code just gushes out. Any disruption to the flow leads to interruptions and ultimately to little code being produced in the time available or to the quality of the code not being optimal. After every interruption, the developer has to pick up speed again and get back into the flow. Surprises are disruptions. They lead to interruptions and errors. Here is an example: If the key assignment in the development environment is such that a usual key combination such as Ctrl-C has a completely different meaning, this hinders the developer. A developer will be annoyed every time he uses the "wrong" key combination. This hinders creative work.
Software should be implemented with few surprises. If a query method called
GetValue() not only provides a value, but also changes the state of the system, the developer will avoid this method in the best case, as he expects unpleasant surprises. In the worst case, he will not notice this strange behavior in time. (Query methods that change the state violate the
Command Query Separation principle). Test-driven development promotes interfaces with few surprises, as the interface is designed and implemented from the perspective of its use.
Information Hiding Principle
Why?
By hiding details in an interface, dependencies are reduced.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
When designing an interface, you should ask yourself which details must be visible externally. By interface, we do not only mean interfaces in the object-oriented sense, but also implicit interfaces. Every class inevitably has an implicit interface - it contains all externally visible details. The more details are visible from the outside, the higher the coupling between the class and its users. Once the users of a class use a detail, it becomes more difficult to change this detail. This is contrary to the changeability of the software.
Practices
Automated Unit Tests
Why?
Only automated tests are actually executed consistently. The more precisely you test code, the better.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
In the
orange degree we introduced integration tests, now it's time for unit tests. In contrast to integration tests, unit tests test a single functional unit (mainly classes, but also methods or components) in isolation. To do this, it is necessary to be able to free this functional unit from its dependencies. If unit tests are to be added to existing code retrospectively, refactoring is often necessary. The integration tests give us the certainty that we are not introducing any errors.
Automated tests offer two benefits:
- You save time
- They take away fear
The more a code base changes, the more time can be saved. Because where code changes, new and old (regression tests) have to be tested again and again. Automation simply saves time. And the more complex the code, the greater the reduction in anxiety. Because if complex code is to be changed - to add functionality, to optimize it or simply to correct it - there is a high risk of unintentionally introducing errors. However, small-step automated tests uncover these, so there is no reason to be afraid of "making things worse".
See also under
Tools.
Mockups
Why?
No easily controllable tests without dummies.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
As a rule, components use other components. If you want to test a component in isolation, these dependencies must be separated. We are now only interested in the functionality of the component to be tested (
System Under Test (SUT)). And we are interested in how the component interacts with the others.
When isolating, we use so-called mockups. These are used instead of the real components. In this way, the system under test interacts with easily controllable mockups instead of real components during the tests.
The literature knows other terms for mockups such as
Stub,
Dummy or
Fakewhich are sometimes used synonymously with mockup, but can be used for
Different modes of operation stand. Before using a mock framework such as
Rhino Mocks you should first implement a mockup "by hand". This helps to understand the mechanism.
See also under
Tools.
Code Coverage Analysis
Why?
Only trust tests that you know really cover the test area.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
If possible, unit tests should cover all paths through our code. This is the only way to gain confidence that the code is working correctly. To find out which code areas are not yet covered by tests, we use the
Code Coverage Analysis. This is used to uncover areas in the code that are not yet executed during the automated tests.
Unit tests should actually cover 100% of the code to be tested. Although this does not automatically mean that sufficient tests exist, less than 100% code coverage indicates that there are still pockets of code about which no correctness statement can yet be made. 100% code coverage should therefore always be aimed for.
In practice, however, it turns out that 100% code coverage cannot always be achieved with an immediately justifiable amount of effort. As elsewhere in life, the effort for the last 2,3,4 percent can grow disproportionately. Therefore, after careful analysis of the coverage situation, it may be acceptable to be satisfied with less than 100%.
Below 90%, however, the coverage is so patchy that it can be considered unprofessional. So if you start with automatic tests, you should always measure the code coverage at the same time. Otherwise, no statement can be made about the quality of the tests.
There are two simple metrics for measuring code coverage, known as C0 and C1 metrics. The C0 key figure measures the statement coverage, whereas the C1 key figure measures the decision coverage or branch coverage.
C0 = (number of instructions tested / number of total instructions) * 100%
C1 = (number of decisions or branches tested / number of total decisions or branches) * 100%
C1 is the stronger key figure, as 100% decision coverage or branch coverage implies 100% statement coverage. The reverse is not true.
The statement coverage test and the branch coverage test work on the basis of a control flow graph, see
http://de.wikipedia.org/wiki/Kontrollflussgraphwhile the decision coverage test is based directly on the source code. The test procedures statement coverage test and branch coverage test are very well described under
http://de.wikipedia.org/wiki/Kontrollflussorientierte_Testverfahren described.
See also under
Tools.
Participation in professional events
Why?
We learn best from others and in community.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
In order not to just "stew in your own juice", it is important to regularly discuss and exchange experiences with other software developers. In order to think outside the box, the exchange should take place with developers outside your own team, your daily routine. User groups that can be found in all regions of Germany are well suited for this.
Regional user groups focus on the exchange of experience. This is important. However, the longer this takes place within the same group, the better you know the people you are talking to, the more the opinions within a user group will converge. That's why it's important to keep thinking outside the box. National developer conferences offer new food for thought and discussions with completely different developers.
A CCD should therefore keep an eye on three levels for the exchange of ideas and inspiration: its own development team, the regional user group and the supra-regional conference. Each level has its own rhythm: daily, monthly, annually.
Left:
Complex refactorings
Why?
It is not possible to write code directly in the ultimate form.
Changeability |
|
Correctness |
|
Production efficiency |
|
Continuous improvement |
|
Single Developer |
Already in the
red degree simple refactorings have been introduced. But
Rename and
Extract method are not enough to improve the code - major interventions are often required. The division into simple and complex refactorings makes sense because complex refactorings can only be carried out efficiently and risk-free with existing automated tests. Without tests, it would not be known after refactoring whether the code is still correct.
See also under
refactoring-legacy-code.net and under
Tools.
It continues with the
green degree