Principles can be checked by the static Principles class. Each method requires at least the dependency Model as Argument. Unlike the Verifier class none of the principles deliberately throws an exception. They return collections of detected violations instead.I choose this behavior, since you generally want to refine the results by your own rules. Each of the methods searches for violating candidates among the whole solution (modules where the property IsPartOfProject is true), because most of them require information of the whole project in order to determine, whether a single entity contradicts with the rules. Under some circumstances you might allow being relaxed with principles. You have assemblies that deal with arithmetics, others with network communication or user interface. I leave it up to your experience to decide, if you treat them equals.
The dependency graph of packages (modules and submodules) must have no cycles. Cycles increase the work to re-build and eventually make every package depend on every other package. Return strongly connected subgraphs in the dependency graph of submodules. Those describe cycles. Cyclic dependencies on module level are already covered by your compiler.
The principle analyzes the graph of submodules. Cycles between modules of course are valiated by your compiler. When you have to press F6 / CTRL+SHIFT+B twice per build, then you know where it comes from. On the other hand detecting cycles between classes is irrelevant in terms of software architecture. The package dependencies should describe an acyclic directed graph, while classes inside a package should work together and should change together, check for Relational Cohesion RC or Association Between Classes ABC metrics.
Repeated code unnecessarily inflates the code base. It may also hide the real purpose of a method since it then fulfills multiple tasks. Even worse is the impact on maintainability. Changes to one occurence are not transferred
to all other places. The code base ends up with slightly diverging implementations of the same logic.
Duplicated code inside the same class or in sibling classes can often be solved by extracting a method. Duplication in unrelated classes is candidate for the extract class refactoring. The method determines possible duplications by comparing the instructions
of each method. There are usually many more instructions than lines of code. A threshold for minimalLength to query is about 40.
The law of Demeter limits the interaction between classes in a system. It supports loose coupling of components since access to distant classes is prohibited.
A method of an object is allowed to invoke the following kinds of objects:
In general, call chains like "object.Something().Foo().Bar()" are forbidden. However, it is allowed to access members of data objects in a chain as long as the chain starts with a data object. Builder pattern is allowed, too.
Allowed:
Forbidden:
When the type Tchild is derived from Tparent, then any behavioral assumption that can be made about an object of the mutable type Tparent must also hold for objects of the derived type Tchild. Otherwise these types cannot be exchanged without altering the context they live in.
DependencyAnalysis implements a validation for LSP of members and types. For types so far, the ruleset is pretty simple. The rules might be extended over time:
An overridden member obviously violates the Liskov Substitution Principle when one of the following applies:
Methodenreinheit is term from mathematics, which describes the preference for solutions that do not introduce foreign concepts to solve a problem. For a programmers solution a foreign concept is the change of something outside the method. A method is considered pure, when its execution has no recognizable side effecst on its environment.
Whenever reproducability is a requirement for a methods outcome you should consider the effects a method has on the whole program. Calling multiple times. Conversions of course must be pure. So should any method that is supposed to retrieve data, while it does not imply setting some state. Especially, this is true for an exposed API, where the caller is not familiar with the internals of your code. Callers rely on you following the principle of command-query-separation.
Code contracts are one use case, which enforce you to manually annotate method with the PureAttribute, though they do not check validity of an annotation.
Before parallelizing tasks you should be aware of sections that can influence other threads. With pure methods you are enabled to eliminate critical sections.
Common coupling is poison for automated tests, methods modifying commons (static members, global variables), while others are reading them. You sometime hear things like: “These tests are sometimes red, there is an issue with the order they are executed.” In fact the order is not the issue. Executing them in random order reveals the issue. The tests either are not isolated from each other or they cannot be isolated due to coupling resulting from impure methods.
The enumeration Purity is a set of flags, where Pure is zero. Any method can be marked with multiple flags, which define different reasons why the method is not pure. Only methods of the current project are considered. Methods in framework or foreign modules are not analyzed.
Allowed for Purity.Pure:
Nothing of the following must be true:
When Methodenreinheit is called for a member other than a method then the result will be Purity.Undefined.
Note: The principle method EvaluateMethodenreinheit determines the purity value for all methods in the current project. By default the value of MemberDescriptor.Purity is initialized with Undefined. Only after executing Methodenreinheit the property is set.
The dependencies between packages should be in the direction of the stability of the packages. A package should only depend upon packages that are more stable than it is. Stability describes the likelihood for a package of not
changing.
Find any dependencies between modules or between submodules, where the target of the dependenc is less stable than the source.
Egg and Gherkin is a development tool written in C# to qualify the evolvement of your software architecture within predefined limits. Controlled by the unit test framework of your choice, it gives immediate feedback when breaking architectural constraints.