User Tools

Site Tools


about:navigating_principle_languages

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
about:navigating_principle_languages [2013-09-09 22:28] – example... christianabout:navigating_principle_languages [2013-09-16 17:27] (current) – decision christian
Line 4: Line 4:
  
 When making a design decision based on principles, it is necessary to find those principles which fit to the given design problem. This means the [[glossary:designer]] has to figure out which aspects need consideration. Seasoned designers will already know that by experience but there is also some guidance for that task. [[glossary:Principle languages]] interconnect principles in a way that the consideration of one principle automatically leads to other principles which are likely to be relevant in the same design situations. They point to other aspects to consider (complementary principles), to possibly downsides (contrary principles), and to principles of different granularity which might fit better to the given problem (generalizations and specializations). When making a design decision based on principles, it is necessary to find those principles which fit to the given design problem. This means the [[glossary:designer]] has to figure out which aspects need consideration. Seasoned designers will already know that by experience but there is also some guidance for that task. [[glossary:Principle languages]] interconnect principles in a way that the consideration of one principle automatically leads to other principles which are likely to be relevant in the same design situations. They point to other aspects to consider (complementary principles), to possibly downsides (contrary principles), and to principles of different granularity which might fit better to the given problem (generalizations and specializations).
- 
- 
-===== Characterizing Sets ===== 
- 
-A principle language cannot free the designer from actually taking a decision. There is no algorithm which replaces a sound judgment. Rather principle languages point to the relevant aspects to consider. For a given design problem, the result is a characterizing set of principles which describes the dimensions of the design space, i.e. the advantages and disadvantages of possible solutions. A typical characterizing set has around five principles. 
- 
-There are two typical questions which can be answered using a characterizing set: 
- 
-  - Given a design problem and a solution, is the solution good? 
-  - Given a design problem and several solutions which one is the best, i.e. the appropriate one? 
- 
-In the first case the solution can be rated according to the principles in the characterizing set. If the benefits justify the liabilities, the solution is appropriate. Otherwise it is worthwhile to think about alternatives.  
- 
-For the second question all the solutions are rated. The solutions which are not pareto-optimal (see [[conflicting principles]]) are clearly bad solutions. The others are all "good" but have different downsides and benefits. The characterizing set of principles shows which they are and helps reasoning about which solution to take.  
- 
-===== Weighting Principles ===== 
- 
-Note that it's not the number of principles which is essential. Weighting the principles and taking the decision is still the task of the designer. Sometimes the weights may be derived from the requirements. Where this is not possible the weighting is an expression of the personal style of the designer, the team or the project.  
- 
-The [[principles:Don't Repeat Yourself|DRY]] principle is a typical example of a principle which might be weighted differently. Virtually everyone will agree that DRY is valid. But there are also downsides of DRY code. The wiki page lists [[principles:Keep It Simple Stupid|KISS]] as a contrary principle. Removing duplication typically has the downside of increased complexity. But how much complexity is justified for making code less redundant? Moving duplicate code to an extracted method creates a further level of indirection but is typically still quite light-weight. But refactoring code according to DRY sometimes also means to extract a new base class. And for some forms of duplication, even code generators are necessary adding further complexity to the build process. 
- 
-Certainly some people will rather favor KISS and others will prefer DRY. Some will say one duplication is tolerable and you should only refactor when there are three similar pieces of code (see [[principles:Rule Of Three]]). But others will say that every tiny bit of duplication demands refactoring and even complex code generators are justified if they ensure that there is a  single, unambiguous, authoritative representation for each piece of information (see [[principles:Rule Of Generation]]). Neither position is wrong and to some extent this kind of weighting is just personal style. Nevertheless according to [[principles:Uniformity Principle|UP]] it is a good idea to roughly agree on some weighting in a team or for a specific project. 
  
  
Line 58: Line 36:
 ===== Example ===== ===== Example =====
  
-The following example shows the usage of the OOD Principle Language. It details the assessment of a solution found in the CoCoME system((http://www.cocome.org/)). The details of the system are irrelevant here but it resembles an information system which can be found in supermarkets or other stores. There are several components which are grouped into the typical layers of an information system: The presentation layer (GUI), the application or business logic layer and the data layer.+==== Context ==== 
 + 
 +The following---rather sophisticated---example shows the usage of the OOD Principle Language. It details the assessment of a solution found in the CoCoME system((http://www.cocome.org/)). The details of the system are irrelevant here but it resembles an information system which can be found in supermarkets or other stores. There are several components which are grouped into the typical layers of an information system: The presentation layer (GUI), the application or business logic layer and the data layer.
  
 In CoCoME there is a mechanism for getting access to other components. In a nutshell it works like this: In CoCoME there is a mechanism for getting access to other components. In a nutshell it works like this:
   * For the data layer there is a data component, a class ''DataImpl'' which aggregates three subcomponents ''Enterprise'', ''Persistence'', and ''Store'' and gives access to them.   * For the data layer there is a data component, a class ''DataImpl'' which aggregates three subcomponents ''Enterprise'', ''Persistence'', and ''Store'' and gives access to them.
  
-FIXME code for Data class+<code java> 
 +public class DataImpl implements DataIf  
 +
 +    public EnterpriseQueryIf getEnterpriseQueryIf()  
 +    { 
 +        return new EnterpriseQueryImpl(); 
 +    } 
 + 
 +    public PersistenceIf getPersistenceManager()  
 +    { 
 +        return new PersistenceImpl(); 
 +    } 
 + 
 +    public StoreQueryIf getStoreQueryIf()  
 +    { 
 +        return new StoreQueryImpl(); 
 +    } 
 +
 +</code>
  
   * There are "factory" classes which lazily create and provide access to single instances of a component.   * There are "factory" classes which lazily create and provide access to single instances of a component.
Line 73: Line 71:
     private static DataIf dataaccess = null;     private static DataIf dataaccess = null;
  
-    private DataIfFactory () {}+    private DataIfFactory() {}
  
-    public static DataIf getInstance () +    public static DataIf getInstance() 
     {     {
         if (dataaccess == null)          if (dataaccess == null) 
         {         {
-            dataaccess = new DataImpl ();+            dataaccess = new DataImpl();
         }         }
         return dataaccess;         return dataaccess;
Line 88: Line 86:
 Essentially ''DataIfFactory'' resembles a mixture between the design patterns [[patterns:factory]] and Essentially ''DataIfFactory'' resembles a mixture between the design patterns [[patterns:factory]] and
 [[factory:singleton]]. The latter one is important here. The purpose of a singleton is to make a single instance of a class globally accessible. Here ''DataImpl'' is not ensured to be only instantiated once as it still has a public constructor. Nevertheless the "factory" class makes it globally accessible. In every part of the software ''DataIfFactory.getInstance()'' can be used to get hold of the data component. And since DataIf makes the three subcomponents accessible, also these are accessible from everywhere. There is no need to pass a reference around. [[factory:singleton]]. The latter one is important here. The purpose of a singleton is to make a single instance of a class globally accessible. Here ''DataImpl'' is not ensured to be only instantiated once as it still has a public constructor. Nevertheless the "factory" class makes it globally accessible. In every part of the software ''DataIfFactory.getInstance()'' can be used to get hold of the data component. And since DataIf makes the three subcomponents accessible, also these are accessible from everywhere. There is no need to pass a reference around.
 +
 +==== Question ====
  
 //Is this a good solution?// //Is this a good solution?//
  
-{{ :collections:ood_principle_language.png?300 |}}+==== Finding a Characterizing Set ==== 
 + 
 +{{ :collections:ood_principle_language.png?400 |}} 
 + 
 +We will examine this question using the OOD principle language. First we have to find suitable starting principles. This is one of the rather sophisticated cases where finding a starting principle is at least not completely obvious. If we don't have a clue where to start, we'll have a look at the different categories of principles in the language. Essentially the "factory" enables modules to access and communicate with each other. So we are looking for principles about module communication. There are three of them in the principle language: [[principles:Tell, don't Ask/Information Expert|TdA/IE]], [[principles:Low Coupling|LC]], and [[principles:Dependency Inversion Principle|DIP]]. TdA/IE does not seem to fit, but LC seems to help. Coupling should be low and the mechanism couples modules in a certain way. So we'll choose LC as a starting principle and our characterizing set looks like this: {LC}. 
 + 
 +Now we'll have a look at the relationships. LC lists [[principles:Keep It Simple Stupid|KISS]], [[principles:High Cohesion|HC]], and [[principles:Rule of Explicitness|RoE]] as contrary, [[principles:Tell don't Ask/Information Expert|TdA/IE]], [[principles:Model Principle|MP]], and [[principles:Information Hiding/Encapsulation|IH/E]] as complementary, and [[principles:Dependency Inversion Principle|DIP]] as a specialization. Let's examine them: 
 + 
 +  * KISS 
 +    * The mechanism may be considered simple or complex, so KISS qualifies 
 +    * We'll insert it into the characterizing set: {LC, KISS} 
 +  * HC 
 +    * The mechanism does not have any influence on cohesion; HC does not qualify 
 +  * RoE 
 +    * The access to the data modules is gained implicitly, so RoE fits 
 +    * New characterizing set: {LC, KISS, RoE} 
 +  * TdA/IE 
 +    * Getting hold of a data module requires chained gets: ''DataIfFactory.getInstance().getStore().doSomething()'' 
 +    * Although we first did not think about TdA/IE, it qualifies; We'll add it: {LC, KISS, RoE, TdA/IE} 
 +  * MP 
 +    * There is no obvious model involved 
 +  * IH/E 
 +    * Does not quite fit. We might think about the data component (not) hiding some information but we can regard that a bit far-fetched.  
 +  * DIP 
 +    * Does not qualify let alone replace LC. 
 + 
 + 
 +So up until now the characterizing set is {LC, KISS, RoE, TdA/IE}. Now let's examine the relationships of the newly added principles. KISS lists [[principles:Generalization Principle|GP]], [[principles:Murphy's Law|ML]] and [[principles:Model Principle|MP]] as contrary principles and [[principles:More Is More Complex|MIMC]] as a specialization.  
 + 
 +  * GP 
 +    * Does not qualify. 
 +  * ML 
 +    * Certainly it is relevant to think about what can go wrong, so ML qualifies. 
 +    * {LC, KISS, RoE, TdA/IE, ML} 
 +  * MP 
 +    * Already examined above. 
 +  * MIMC 
 +    * KISS fits better 
 + 
 +Characterizing set up until now: {LC, KISS, RoE, TdA/IE, ML}. ML was newly added. Maybe on this point we might decide to abort the process because we already have a good idea of the aspects. But for the sake of the example, we'll continue with the relationships of ML. The wiki page lists KISS as a contrary principle and [[principles:Don't Repeat Yourself|DRY]], [[principles:Easy to Use and Hard to Misuse|EUHM]], [[principles:Uniformity Principle|UP]], and [[principles:Invariant avoidance Principle|IAP]] as specializations. 
 + 
 +  * KISS 
 +    * Already examined above. 
 +  * DRY 
 +    * There is no duplication. DRY does not qualify. 
 +  * EUHM 
 +    * ML fits better. 
 +  * UP 
 +    * Whether UP qualifies or not, depends on the rest of the system, the team, other projects, etc. For the example, we'll assume that there are no other projects and parts of the system we want to be consistent with. 
 +  * IAP 
 +    * Does not fit better and adds no valuable aspect. 
 + 
 +As a result we get {LC, KISS, RoE, TdA/IE, ML} as the characterizing set. 
 + 
 +Note that although in this example the principles are examined in a certain order, the method does not prescribe any. 
 + 
 + 
 +==== Using the Characterizing Set ==== 
 + 
 +In order to answer the above question, we have to informally rate the solution based on the principles of the characterizing set: 
 + 
 +  * LC 
 +    * The solution creates a relatively strong coupling to the concrete implementations of the components. If a class uses the "factory" and the components it gives access to, there is no way to have the class use other implementations of the components. There is no way to replace the implementations by a stub for testing purposes, there is no way to smoothly switch to another ''Data'' component possibly using another way of storing the data. Every change in the arrangement of the classes needs a change in the code. LC is rather against this solution. 
 +  * KISS 
 +    * The solution is pretty easy to implement. Furthermore it is easy to get access to an arbitrary component. So according to KISS this is a good solution. 
 +  * RoE 
 +    * Getting access to a component is implicit. There is no need to explicitly pass a reference around. There is not even the necessity to explicitly define an attribute for the dependent class. RoE tells, that the solution is bad. 
 +  * TdA/IE 
 +    * Getting access to a the ''Store'' subcomponent requires asking ''DataIfFactory'' for the ''Data'' component and asking that one for the store. There is no way to tell the "factory" to do something. TdA/IE is against the solution. 
 +  * ML 
 +    * There are no particular pitfalls with this solution. So ML has nothing against it. 
 + 
 +So LC, RoE and TdA/IE are against the solution, KISS thinks it's good and ML has nothing against it. As it is not the number of principles which is important, the designer still has to make a sound judgment based on these results. What is more important: Coupling, testability, and clarity or a simple and fast implementation. In this case we'd rather decide that the former are more important, so we should rather think about a better solution. 
 + 
 +==== Deciding between Alternatives ==== 
 + 
 +In the next step we would think about better alternatives and might come up with [[patterns:dependency injection]] and [[patterns:service locators]]. So there are three alternatives (with several variations): The current solution and the two new ideas. 
 + 
 +We already constructed a characterizing set. So the only thing to do is to rate the ideas according to the principles: 
 + 
 +The current "factory" approach is abbreviated "F", dependency injection is DI and SL stands for service locator. In the following a rough, informal rating is described, where "A > B" means that the respective principle rates A higher/better than B. "=" stands for equal ratings. 
 + 
 +  * LC 
 +    * DI > SL > F  
 +    * Note that in the SL approach there is an additional coupling to the service locator 
 +  * KISS 
 +    * F > DI = SL 
 +    * All three solutions are rather simple but in DI there is complexity for passing around the references and in the SL approach there is complexity in maintaining the registry 
 +  * RoE 
 +    * The rating of RoE depends on the concrete variant of the pattern. In the DI approach the dependencies are explicitly visible on the interface, which is not the case in the two other approaches. In solution F the dependency is not visible from the interface at all. Same with SL if the service locator is globally accessible. Even if a reference to the service locator is explicitly passed around, it is still not visible which services provided by the locator are used. On the other hand getting a reference is explicit with F and SL. In the DI approach it is only explicit when it is done manually. Typical DI frameworks wire the instances implicitly. 
 +  * TdA/IE 
 +    * DI > SL = F 
 +    * For SL and F one first has to ask for an instance for calling a method on it. In DI the instance is already known, i.e. set from the outside. 
 +  * ML 
 +    * F > DI > SL 
 +    * In the DI solution a possible fault would be to have different modules reference different instances of the same class where they should rather reference the same instance. In SL solution there is even a more problematic fault which could be introduced. Eventually somebody might get the idea to change the registered instances in the locator at runtime. This would then be the source for some hard to find defects: Some modules will cache the instance they got from the service locator in an attribute and some won't. In such a case the latter will receive the new instance while the former won't. 
 + 
 +As you can see all three possibilities have their advantages and disadvantages. The designer now has to weight the aspects in order to get to a decision. In this case we might state the following:
  
-We will examine this question using the OOD principle languageFirst we have to find suitable starting principlesThis is one of the rather sophisticated cases where finding starting principle is at least not completely obviousIf we don'have a clue where to start, we'll have look at the different categories of principles in the language. Essentially the "factory" enables modules to access and communicate with each other. So we are looking for principles about module communication. There are three of them in the principle language: [[principles:Tell, don't Ask/Information Expert|TdA/IE]], [[principles:Low Coupling|LC]]and [[principles:Dependency Inversion Principle|DIP]]TdA/IE does not seem to fitbut LC seems to help. So our characterizing set first looks like this: {LC}.+  * F is ruled out because it is not testableThe other two approaches have lower couplings (LC) which make them better testable. The advantages wrt. KISS and ML do not justify that liability. 
 +  * The solutions DI and SL are not very far apart but DI is slightly better wrt. LC, TdA/IE and ML.  
 +  * TdA/IE can be regarded less important because it is a heuristic which is normally applied in other situations. 
 +  * For RoE we also have to decide whether to use framework or not. In CoCoME we would rather want to avoid framework because the rest of the system is implemented in that way ([[principles:Uniformity Principle|UP]]). We could incorporate that aspect in the characterizing principle or make another decision based on a newly created one. If we are comfortable with making the decision without constructing a characterizing setjust based on UPwe could also do thatIn order not to complicate the example and in order to show this possibilitywe'll do the latter. So a framework won't be used and manual dependency injection is good wrt. RoE.
  
-Now we'll have a look at the relationships. LC lists [[principles:Keep It Simple Stupid|KISS]], [[principles:High Cohesion|HC]], and [[principles:Rule of Explicitness|RoE]] as contrary, [[principles:Tell don't Ask/Information Expert|TdA/IE]], [[principles:Model Principle|MP]], and [[principles:Information Hiding/Encapsulation|IH/E]] as complementary, and [[principles:Dependency Inversion Principle|DIP]] as a specialization.+Based on this weighting, we decide to use DI.
about/navigating_principle_languages.1378758522.txt.gz · Last modified: 2013-09-09 22:28 by christian