Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Thursday, 04 June 2015

dsa.msc – Start AD Users

| | # 

Strengthen the guarantees of the methods with DirectoryInfo and FileInfo instead of strings

DirectoryInfo

public FileStore(DirectoryInfo workingDirectory)

Instead of accepting String, be more explicit.
public FileInfo GetFileInfo(int id)

Handy as can do
var file = this.GetFileInfo(id); if (!file.Exists)

FileInfo

public FileInfo GetFileInfo(int id) { // This can never be null as int is a value type, and workingDirectory is a pre-condition return new FileInfo(Path.Combine(this.WorkingDirectory.FullName, id + ".txt")); }

Again more prescriptive of the return type of the method.. so can use
FileInfo fileInfo = fileStore.GetFileInfo(id); string fileName = fileInfo.FullName;


Caching and Logging
public void Save(int id, string message){ Log.Information("Saving message {id}", id); var file = this.GetFileInfo(id); File.WriteAllText(file.FullName, message); this.cache.AddOrUpdate(id, MessageTemplate (i,s) => message); }

public Maybe<string> Read(int id){ var file = this.GetFileInfo(id); if (!file.Exists) return new Maybe<string>(); var message = this.cache.GetOrAdd(id, _ => File.ReadAllText(file.FullName)); //var message = File.ReadAllText(file.FullName); // Never want message to be Null - that is what the previous step is for return new Maybe<string>(message); }

Caching and Logging behaviour – common additions to code.

Where to Start – Logging

| | # 
# Monday, 01 June 2015
( SOLID )

 

  • A class should have only 1 reason to change  (do 1 thing and do it well)
  • Separation of concerns eg caching/logging
  • So a class should be 1 thing and do it well

How many reasons for change does the FileStore class have?

  • Logging
  • Caching
  • Storage (eg not filesystem use db)
  • Orchestration

image_thumb2

1. Logging - How to refactor – decompose each reason for change to other Classes

Move logging to another class – Serilog

public class StoreLogger { public void Saving(int id) { Log.Information("Saving message {id}.", id); } public void Saved(int id) { Log.Information("Saved message {id}.", id); }

and then:

public void Save(int id, string message) { this.log.Saving(id); //... this.log.Saved(id);

Test app working

http://xunit.github.io/docs/getting-started.html

xunit.net
xunit.runner.console

xUnit.net console test runner (64-bit .NET 4.0.30319.34209)
Copyright (C) 2015 Outercurve Foundation.

Discovering: SRPTests
Discovered:  SRPTests
Starting:    SRPTests
Finished:    SRPTests

=== TEST EXECUTION SUMMARY ===
   SRPTests  Total: 1, Errors: 0, Failed: 0, Skipped: 0, Time: 0.292s

packages\xunit.runner.console.2.0.0\tools\xunit.console SRPTests\bin\Debug\SRPTests.dll

xunit.runner.visualstudio

Serilog – having trouble getting it to compile from nuget

install-package serilog

gets 1.5.6

Got it working by starting a new project, then adding in logging first.  SerilogTest

using Serilog; namespace SerilogTest { public class FileStoreLogger { public void Save(string message) { var log = new LoggerConfiguration() .WriteTo.ColoredConsole() .WriteTo.RollingFile(@"C:\Temp\Log-{Date}.txt") .CreateLogger(); log.Information("Saving"); } } }

and

using Xunit; namespace SerilogTest { public class Tests { [Fact] public void Thing() { var fileStoreLogger = new FileStoreLogger(); fileStoreLogger.Save("test"); Assert.Equal(1, 1); } } }

which dumped a log file into c:\temp\

Xunit2 not picking up tests

Create project
compile
restart vs
install-package xunit
compile
restart vs
install-package xunit.runner.visualstudio
restart vs

install-package seriolog

install-package AutoFixture.XUnit2

| | # 
# Friday, 29 May 2015
( Git )

git log -1 
This gives the latest SHA1

git diff
See the latest diffs

| | # 
( SOLID )

Supple/Pliable code – can change shape as we go along (as requirements always change)

  • Purpose of SOLID is to make you more productive
  • ..by making code more maintainable
  • ..through decomposition and decoupling

SOLID is a reaction to code smells

  • Rigidity – the design is difficult to change
  • Fragility – the design is easy to break
  • Immobility – the design is difficult to reuse
  • Viscosity – difficult to do the right thing
  • Overdesign – needless complexity

The 5 Principles (Uncle Bob first described)

  • Single Responsibility (SRP)
  • Open Closed (OCP)
  • Liskov Substitution (LSP)
  • Interface Segregation (ISP)
  • Dependency Inversion (DIP)
| | # 

To get XUnit2 working with AutoFixture using R# test runner:

Run powershell as administrator: Set-ExecutionPolicy RemoteSigned

Install-Package AutoFixture.XUnit2   **be careful not to install AutoFixture.Xunit
(This installs AutoFixture and XUnit2)

Install-Package xunit.runner.visualstudio
(Test runner for xunit)

Install-Package xunit

https://github.com/djhmateer/githubscratch/tree/master/MaybeTest2

| | # 
# Thursday, 28 May 2015

https://github.com/djhmateer/githubscratch/tree/master/maybe

I like the concept behind Maybe<T>..  Option Type in functional programming. 

  • List/Collection of 0 or 1 elements
  • eg if a TextFileRead operation is legal it returns the contents of the file as 1 element
  • eg if a TextFileRead operation fails it returns 0 elements

[Theory, AutoData] //[Theory] //[InlineData("test message")] public void ReadReturnsMessage(string message) { var fileStore = new FileStore(Environment.CurrentDirectory); fileStore.Save(44, message); Maybe<string> actual = fileStore.Read(44); Assert.Equal(message, actual.Single()); }

To get these tests to run, I had to install: (commented out text above shows how to run it not using AutoFixture)

https://www.nuget.org/packages/xunit/1.9.2 – not version 2

https://www.nuget.org/packages/xunit.extensions/1.9.2 – not version 2

https://www.nuget.org/packages/AutoFixture/ – used latest

https://www.nuget.org/packages/AutoFixture.Xunit/ – used latest

AutoFixture puts in random data into the string message.

Also found I had to use R#9 to get xunit2 runner working (although using xunit1 now)

| | # 
# Wednesday, 13 May 2015

Methods that return something…(Queries)

public string GetFileName(int id) { // This can never be null as int is a value type, and workingDirectory is a pre condition return Path.Combine(this.WorkingDirectory, id + ".txt"); }

Null is never a valid return value (a good design decision)
// Could return a null/empty string if 1) no message 2) exception is thrown public string Read(int id) { var path = this.GetFileName(id); if (!File.Exists(path)) throw new ArgumentException("bad path", "read"); var message = File.ReadAllText(path); return message; }
3 ways to approach this problem of – “is it legal to invoke this operation?”

Tester/Doer

Always pairs.. the first is a ‘test’ to see if the operation is valid

public bool Exists(int id){ var path = GetFileName(id); return File.Exists(path); }

then

// client invokes string message = ""; if (fileStore.Exists(49)) message = fileStore.Read(49);

So, ok but not brilliant as need to know the idiom.  Also not thread safe

TryRead (or TryParse idiom)

// 2.TryRead (TryParse) public bool TryRead(int id, out string message){ message = null; var path = this.GetFileName(id); if (!File.Exists(path)) return false; message = File.ReadAllText(path); return true; }

Can be made threadsafe.  Not very OO – can’t have a fluent interface on top of this as TryRead returns a bool.

Maybe

Borrowed from functional (option type).. so it’s a collection with 0 or 1 elements

// Return value may be there, and may not be // and don't want to return null as it is tainted // want a collection of 0 or 1 strings // IEnumerable<string> is too wide as it could have 10 elements // Maybe<T> can return 0 of 1 <T> public Maybe<string> Read(int id) { var path = this.GetFileName(id); if (!File.Exists(path)) return new Maybe<string>(); var message = File.ReadAllText(path); return new Maybe<string>(message); }
public void Tester() { // this method may, or may not return a string Maybe<string> thing = Read(49); var message = thing.DefaultIfEmpty("").Single(); }

Synthesis/Summary

Making it easier for colleagues (and yourself in the future) to use your classes

  • Split methods into Commands (eg Save return void) and Queries eg GetFileName
  • Fail fast, and give good exception message
  • Never return null.. if need the concept of a value not being present then
    • Read returns a Maybe of string.. invalid states are impossible

 

Maintainability and Productivity improved

Decompose operations that return a value and one that has a side effect

| | # 

Robustness principle

  • Conservative in what you send (give good guarantees)
  • Liberal in what you accept
    • fail fast if can’t understand

Input

var fileStore = new FileStore();
fileStore.Save(42, “Hello world”); // Throws

As FilePath on the object is null.  So will fail/throw at runtime.

Could make an explicit constructor, so.

public FileStore(string workingDirectory) { // Guard clause as string is a ref type, so could be set to null outside if (workingDirectory == null) throw new ArgumentNullException("workingDirectory"); this.WorkingDirectory = workingDirectory; } // Private set public string WorkingDirectory { get; private set; }

Nullable References are Evil

Value types are primitive types like: ints, decimals, bools – non nullable by defaults

Ref types are Classes – nullable by default (non-nullable ref types not possible..language problem)

Fail Fast

// Exception messages are like documentation if (!Directory.Exists(workingDirectory)) throw new ArgumentException("You tried to supply a working directory string which doesn't exist." + " Please supply a valid path to an existing directory", "workingDirectory");

Be as tolerant as possible for inputs.

Be an explicit as possible about problems, and how to fix the problems.

| | # 
# Tuesday, 12 May 2015

http://www.pluralsight.com/courses/encapsulation-solid

Code that Sucks – many programmers in orgs feel their codebase does

Stand on the shoulders of giants (ie use ‘frameworks’ like ORMS etc..)

Make clearer code

public string Save(int id, string message) – what is the return string?? not obvious… need to read source

public void Read(int id).. why does this return void? need to read source

The Impact of Code that sucks

Long term productivity – a few weeks!!.. hard to write new features

Maintainability – if built up lots of technical debt

Encapsulation

  • Information hiding (Implementation hiding is a better name)
    • property / field.. eg password on a user class..maybe want to hide keeping an array of previous ones
  • protection of invariants
    • invalid states are difficult/impossible
    • assertions

Beyond OO

Command Query Separation (CQS) Principal

Bertand Meyer

CQRS – something different..architectural design pattern.. distributed architecture

Commands – have observable side effects eg send an email, write something to disk

Should do 1 or another

Query – returns data

Commands – returns void ie should do something

void Save(Order order); – what does it do? probably saves an order
void Send (T message) -
void Associate (IFoo foo, Bar bar);

All return void… so, it must have a side effect… so must be a command (otherwise what's the point)

Ok to invoke a queries eg

// Command
public void Save(int, id, string message)
{
  // GetFileName is a Query
  var path = this.GetFileName(id);
  File.WriteAllText(path, message);
}

Queries – return data (no side effects)

Do not mutate observable state

Order[] GetOrders(int userId); – probably gets orders associated with that userId
IFoo Map(Bar bar);
T Create(); – returns an instance of T

Idempotent – if invoke n times, it wont change the state of the system

If follow CQS can trust the codebase / easier to reason about the code without fully understanding the implementation details

http://blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids/

| | #