Untitled.Bat |
Developer, dad & dangerously dopey.....Also awesome at alliteration and assonance . Visit my main website @ http://www.xerxesb.com if you're tech-inclined |
Concurrency is a decoupling strategy. It helps us decouple what gets done from when it gets done. (XB: functional programming also achieves this to some degree). Writing concurrent programs is hard.
Some principles to follow when writing concurrent systems:
Concepts used in concurrent programming:
There are several execution models we can employ to partition behaviour in a concurrent application:
Keep Synchronised Sections Small
Locks are expensive because they create delays and add overhead, so we dont want to litter the code with locks. OTOH critical sections must be guarded. Rather than making critical sections very large which increases contention and degrades performance, we should keep synchronised sections as small as possible.
Writing Correct Shut-Down Code Is Hard
Graceful shutdown can be hard to get correct. Common problems include deadlock, with threads waiting for a signal to continue that never comes. EG: a parent which issues a shutdown to many threads, but if two threads are in deadlock the parent is blocked until they terminate - and they never will. It’s important to think about shut-down early and get it working early.
Testing Threaded Code
Write tests which havethe potential to expose problems and then run them frequently, with different programmatic conditions and load configurations. If tests ever fail, track down the failure - dont ignore a failure just because the tests pass on a subsequent run. Make sure your code works in a non-threaded world and then introduce threading. Make the thread-based code pluggable/configurable so that you can run it in various configurations.
Clean Code: Chapter 13 - Concurrency
According to Beck, a design is “simple” if it follows these rules:
Steps 2 to 4 are refactorings we work to incrementally.
Code can be made expressive by:
Clean Code: Chapter 12
Separate Constructing a System From Using It. Constructing a system is very different from using it. Patterns like lazy initialization/evaluation are convenient idioms, but lead to modularity breakdown. The startup process of object construction and wiring should be modularised out from normal runtime logic and we should make sure that we have a global, consistent strategy for resolving our major dependencies. There are a number of techniques we can use to separate construction of objects from their use.
Separation Of Main. This technique involves the main startup block of an application instantiating all required objects upfront, and then when the application runs, the wired up objects are ready to be consumed. Although it sounds hacky, this is actually quite a common paradigm as most of your helper objects are created and ready to use.
Factories are patterns which give the application control of WHEN to build objects. The factory object is initialized at main startup, but the instance itself is created when the application requires it.
Inversion of Control (IoC) moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting SRP. In terms of dependency management, any object should not take responsibility for instantiating dependencies itself. It should instead delegate this responsibility to another authority, thereby inverting control.
Dependency Injection (DI) goes one step further. The class takes no direct stapes to resolve its dependencies; it is complete passive. It provides setter methods or constructor arguments that are uised to inject dependencies, and the DI container instantiates the required objects and uses the constructor arguments or setter methods provided to wire together the dependencies.
Cross Cutting Concerns are concerns which are modular in their nature (like persistence) but must be srpead over a wide area of objects. These concerns cut-across many different layers or concerns within the application.
Aspect Oriented Programming (AOP) is a general-purpose way to support a partiuclar concern, done using a succinct declarative or programattic mechanism.
An optimal system architecture consists of modulearized domains of concern, each of which is implemented with POCOs. The different domains are integrated together with minimally invasive Aspects or Aspect-like tools. This architecture can be test-driven, just like the code.
Domain Specific Languages are small scripting languages or APIs that permit code to be written so that it reads like a structures form of prose that a domain expert might write. A good DSL minimizes the communication gap between a domain concept and the code that implements it, leading to less risk that you will incorrectly translate the domain into the implementation.
Clean Code: Chapter 11
Encapsulation. Classes should maintain as small scope as possible. Loosening encapsulation is always a last resort
Classes should be small. And then classes should be smaller again! With functions we measured size by counting physical lines. With classes we use a different measure. We count responsibilities.
Responsibilities. The name of a class should describe what responsibilities it fulfills. If we cannot derive a concise name for a class, it’s likely to have too many responsibilities. Weasel words like Manager, Processor or Super often hint at aggregation of responsibilities
SRP. The Single Responsibility Principle states that a class should have only one reason to change (ie: require modification)
Cohesion. In general, the more instance variables a method manipulates the more cohesive that method is to its class. When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole. Maintaining cohesion results in many small classes. When classes lose cohesion (ie: number of instance variables grows, but those variables aren’t used by many methods), split them up into smaller classes!
OCP. Classes should be open for extension but closed for modification. In an ideal system, we incorporate new features by extending the system, not by making modifications to existing code. For example, a God class SQL which contains methods to CRUD, could be split into a base SQL class with subclasses for each operation, overriding a base behaviour. To create a new type of SQL operation, you dont need to modify the God class, you create a new class, and inherit from the base.
DIP. Dependency Inversion Principle says that our classes should depend upon abstractions, not on concrete details. This abstraction isolates all of the specific details of any particular implementation away from the caller.
Clean Code: Chapter 10
Three Laws of Unit Testing:
Keep tests clean. Test code is just as important as production code. It is not a second-class citizen. It requires thought, design and care. The only way test code differs from production code is that it need not be as optimised (speed/time) to the degree as production code must. Tests should still be fast, but speed is not a priority - clarity is.
What keeps tests clean? Readability (clarity, simplicity and density of expression).
Uncle Bob writes that he prefers Single Concept Per Test, which may result in multiple assertions per test. Personally, I prefer single assertion per test as it explicitly states that any single test can fail for one and only one reason. Further, having multiple assertions per test means that when a test fails, subsequent assertions aren’t run so you’re not provided a full indication of test-suite success/failure.
Clean tests follow 5 other rules following the acronym F.I.R.S.T:
Fast: Tests should be fast
Independent: Tests should not depend on each other
Repeatable: Tests should be repeatable in any environment
Self-Validating: Tests should have a boolean output - Either they pass or fail
Timely: Tests need to be written in a timely fashion (ie: just before the production code is written)
Clean Code: Chapter 9
Boundaries are defined as code areas which integrate external/third-party components into our system. They are components for which the interface is out of our control.
The detail of external components should be abstracted away from our code, and the external component design encapsulated by our own wrapper.
The design of our wrappers can be determined by creating learning tests. These are unit tests written with the intent of exploring a third-party library when there is a well-defined interface, but it is not known how to use it.
We can design wrappers for external components which are not-yet complete or are rapidly evolving by creating a facade based on the interface we wish to use in our own applications. One good thing about writing the interface we wish we had is that it’s under our control. This helps keep clients code more readable and focused on what is is trying to accomplish.
Clean Code: Chapter 8
When writing exception handling code, it’s important that the exception handling is small, isolated and doesn’t obscure the code. Error handling is important, but if it obscures logic, it’s wrong.
Use exceptions rather than return codes, otherwise you clutter the calling code with IF..ELSE return code checks
Write tests which force exceptions and then add behaviour to your handler to satisfy your tests. This allows you to build the transaction scope of the TRY block first and help maintain the transaction nature of the scope
Provide Context with Exceptions. Exception messages should provide enough context to determine the intent of the code that failed
Define exceptions in terms of the caller’s needs. Wrap classes which could throw numerous different types of exceptions into a single exception which is more usable by the calling code. EG: A port I/O comms library could throw a timeout, device response, port locked etc.. exceptions. Encapsulate these in a single PortDeviceFailure exception and hide details from the caller
Define the normal flow in your code and only throw exceptions when the situation is quite extraneous. When returning objects, gor cases where the situation isn’t quite as dire as to throw an exception, consider using the Special Case Pattern to encapsulate the “null case” as an object and return that instead. http://pastie.org/621114
Don’t return null! It is possibly the worst thing that could be done because it litters code with null reference checks. Instead, consider returning a Special Case Pattern object or throwing an exception. For example, in order.GetItems(), return an empty list, rather than a null if there are no items in the order.
Don’t Pass Null into arguments of methods unless it is clearly documented to do so. Passing null in might result in a NullReferenceException in the called code if it did not expect it. Change the callee to check for null arguments and throw an InvalidArgumentsException if they are null. Another option is to perform an assertion that the arguments are not null, however neither option are particularly good. They will still result in run-time errors, the lesser evil being the InvalidArgumentsException instead of a NullReferenceException
Clean Code: Chapter 7
A module should not know about the innards of the objects it manipulates.
Given a method f() of class C, f is only allowed to call methods on:
The Law of Demeter applies to Objects (not Data Structures), where:
As summarized from Clean Code, Chapter 3: Functions:
I’m documenting this with the belief that some of these will help me in decomposing problems better. I don’t believe these are Cogan-esque rules to be applied come hell or high-water (for instance, throwing an exception from a function which is consumed in a tight loop is just a bad idea, period.).
Keeping them around to jog my memory
We want to be able to read the program as though it were a set of TO paragraphs each of which is describing the current level of abstraction and referencing subsequent to paragraphs at the next level down. [This trick] is the key to keeping functions short and making sure they do “one thing”.
Clean Code: Pg 37