Applying functional
programming approaches in
object oriented languages
Mark Needham
© ThoughtWorks 2010
C# 1.0
int[] ints = new int[] {1, 2, 3, 4, 5}
int[] Filter(int[] ints)
{
ArrayList results = new ArrayList();
foreach (int i in ints)
{
if (i % 2 == 0)
{
results.Add(i)...
int[] ints = new int[] {1, 2, 3, 4, 5}
int[] Filter(int[] ints)
{
ArrayList results = new ArrayList();
foreach (int i in ints)
{
if (i > 3 == 0)
{
results.Add(i)...
int[] Filter(int[] ints)
{
ArrayList results = new ArrayList();
foreach (int i in ints)
{
if (i % 2 == 0)
{
results.Add(i)...
int[] Filter(int[] ints)
{
ArrayList results = new ArrayList();
foreach (int i in ints)
{
if (i >3 == 0)
{
results.Add(i);...
interface IIntegerPredicate
{
bool Matches(int value);
}
class EvenPredicate : IIntegerPredicate
{
bool Matches(int value)
{
return value % 2 == 0;
}
}
class GreaterThan3Predicate : IIntegerPredicate
{
bool Matches(int value)
{
return value > 3;
}
}
int[] Filter(int[] ints, IIntegerPredicate predicate)
{
ArrayList results = new ArrayList();
foreach (int i in ints)
{
if ...
int[] ints = new int[] {1, 2, 3, 4, 5 };
int[] even = Filter(ints, new EvenPredicate());
int[] greaterThan3 = Filter(ints,...
interface IIntegerPredicate
{
bool Matches(int value);
}
bool delegate IntegerPredicate(int value);
bool Even(int value)
{
return value % 2 == 0;
}
bool GreaterThan3(int value)
{
return value > 3;
}
int[] Filter(int[] ints, IntegerPredicate predicate)
{
ArrayList results = new ArrayList();
foreach (int i in ints)
{
if (...
int[] ints = new int[] {1, 2, 3, 4, 5 };
int[] even = Filter(ints, new IntegerPredicate(Even));
int[] greaterThan3 = Filte...
C# 2.0
Inference
int[] ints = new int[] {1, 2, 3, 4, 5 };
int[] even = Filter(ints, new IntegerPredicate(Even));
int[] greaterTha...
Inference
int[] ints = new int[] {1, 2, 3, 4, 5 };
int[] even = Filter(ints, Even);
int[] greaterThan3 = Filter(ints, Grea...
Generics
delegate bool IntegerPredicate(int value);
Generics
delegate bool Predicate<T> (T value);
Generics
int[] Filter(int[] ints, IntegerPredicate predicate)
{
ArrayList results = new ArrayList();
foreach (int i in int...
Generics
T[] Filter<T>(T[] values, Predicate<T> predicate)
{
List<T> results = new List<T>();
foreach (T i in value)
{
if ...
Iterators
IEnumerable<T> Filter<T>(IEnumerable<T> values,
Predicate<T> p)
{
List<T> results = new List<T>();
foreach (T i ...
Iterators
IEnumerable<T> Filter<T>(IEnumerable<T> values,
Predicate<T> p)
{
foreach (T i in value)
{
if (p(i))
{
yield ret...
Anonymous Methods
IEnumerable<int> greaterThan3 = Filter(ints, GreaterThan3);
Anonymous Methods
IEnumerable<int> greaterThan3 = Filter(ints,
delegate(int value) { return value > 3; });
Anonymous Methods
int minimumValue = 3;
IEnumerable<int> greaterThan3 = Filter(ints,
delegate(int value) { return value > ...
C# 3.0
Lambdas
int minimumValue = 3;
IEnumerable<int> greaterThan3 = Filter(ints,
delegate(int value) { return value > minimumVal...
Lambdas
int minimumValue = 3;
IEnumerable<int> greaterThan3 = Filter(ints,
value => value > minimumValue);
More Type Inference
int minimumValue = 3;
IEnumerable<int> greaterThan3 = Filter(ints,
value => value > minimumValue);
More Type Inference
int minimumValue = 3;
var greaterThan3 = Filter(ints, value => value > minimumValue);
Extension Methods
int minimumValue = 3;
var greaterThan3 = Filter(ints, value => value > minimumValue);
Extension Methods
int minimumValue = 3;
var greaterThan3 = ints.Filter(value => value > minimumValue);
LINQ
LINQ
New delegates in System namespace
Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult>
etc.
LINQ
New delegates in System namespace
Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult>
etc.
System.Linq
Extens...
LINQ
New delegates in System namespace
Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult>
etc.
System.Linq
Extens...
LINQ
var even = ints.Where(value => value % 2 == 0)
var greaterThan3 = ints.Where(value => value > 3)
or
var even = from v...
Interfaces
Delegates
Anonymous methods
Lambdas
So this functional programming thing…
http://www.flickr.com/photos/stuartpilbrow/2938100285/sizes/l/
Higher order functions
var ints = new int[] {1, 2, 3, 4, 5 };
var greaterThan3 = ints.Where(value => value > 3)
var even = ints.Where(value => va...
Pure functions
Immutability
Lazy evaluation
Iterators
IEnumerable<T> Filter<T>(IEnumerable<T> values,
Predicate<T> p)
{
foreach (T i in value)
{
if (p(i))
{
yield ret...
Recursion & Pattern Matching
Transformational Mindset
We can just pass functions around instead in most cases
- find an example where it still makes se...
Input -> ??? -> ??? -> ??? -> Output
http://www.emt-india.net/process/petrochemical/img/pp4.jpg
So why should you care?
Functional can fill in the gaps in OO code
Programming in the…
Large
Medium
Small
Programming in the …
Large
Medium
Small
“a high level that affects as well as crosscuts multiple
classes and functions”
Programming in the …
Large
Medium
Small
“a single API or group of related APIs in such things
as classes, interfaces, modu...
Programming in the …
Large
Medium
Small
“individual function/method bodies”
Large
Medium
Small
Large
Medium
Small
Abstractions over common operations means less
code and less chances to make mistakes
Projection
people.Select(person => person.Name)
people.SelectMany(person => person.Pets)
Restriction
people.Where(person => person.HasPets)
Partitioning
people.Take(5)
people.Skip(5)
people.TakeWhile(person =>
person.Name != "David")
people.SkipWhile(person =>
person.Name != "David")
Set
people.Select(person => person.Name)
.Distinct()
people.Union(someOtherPeople)
people.Intersect(someOtherPeople)
people.Except(someOtherPeople)
Ordering and Grouping
people.OrderBy(person => person.Name)
people.GroupBy(person => person.Name)
Aggregation
people.Count()
people.Select(person => person.Age)
.Sum()
people.Select(person => person.Age)
.Min()
people.Select(person => person.Age)
.Max()
people.Select(person => person.Age)
.Average()
people.Select(person => person.Age)
.Aggregate(0, (totalAge, nextAge) =>
nextAge % 2 == 0
? nextAge + totalAge
: totalAge)
Joining
people.Join(addresses,
person => person.PersonId,
address => address.PersonId,
(person, address) => new {
person, address})
Embrace the collection
var first = “Mark”;
var middle = “Harold”;
var surname = “Needham”;
var fullname = first + “ “ + middle + “ “ + surname;
o...
public class SomeObject
{
public SomeObject(string p1, string p2, string p3)
{
if(p1 == null)
throw new Exception(…);
if(p...
public class SomeObject
{
public SomeObject(string p1, string p2, string p3)
{
var parameters = new[] {p1, p2, p3};
if(par...
Large
Medium
Small
We can just pass functions around instead in most cases
- find an example where it still makes sense to use the GOF approa...
public class SomeObject
{
private readonly IStrategy strategy;
public SomeObject(IStrategy strategy)
{
this.strategy = str...
public class Strategy : IStrategy
{
public void DoSomething(string value)
{
// do something with string
}
}
public class SomeObject
{
private readonly Action<string> strategy;
public SomeObject(Action<string> strategy)
{
this.stra...
Hole in the middle pattern
public class ServiceCache<Service>
{
protected Res FromCacheOrService
<Req, Res>(Func<Res> serviceCall, Req request)
{
var...
public class CachedService : ServiceCache<IService>
{
public MyResult GetMyResult(MyRequest request)
{
return FromCacheOrS...
Maybe?
public interface Maybe<T>
{
bool HasValue();
T Value();
}
public class Some<T> : Maybe<T>
{
private readonly T theThing;
public Some(T theThing)
{
this.theThing = theThing;
}
publi...
public class None<T> : Maybe<T>
{
public bool HasValue ()
{
return false;
}
public T Value()
{
throw new NotImplementedExc...
public class Some
{
public static Some<T> Thing<T>(T thing)
: where T : class
{
return new Some<T>(thing);
}
}
public class No
{
public static None<T> Thing<T>()
{
return new None<T>();
}
}
public static class MaybeExtensions
{
public static Maybe<T> Maybify<T>(this T source)
where T : class
{
if(source == null...
public class FooService
{
public Foo FindOrCreate(int fooId)
{
var foo = fooRepository.Find(fooId);
if(foo.HasValue())
{
r...
Continuation Passing Style
static void Identity<T>(T value, Action<T> k)
{
k(value);
}
Identity("foo", s => Console.WriteLine(s));
Identity("foo", s => Console.WriteLine(s));
as compared to
var foo = Identity(“foo”);
Console.WriteLine(foo);
public ActionResult Submit(string id, FormCollection form) {
var shoppingBasket = CreateShoppingBasketFrom(id, form);
retu...
private RedirectToRouteResult IsValid(ShoppingBasket
shoppingBasket,
ModelStateDictionary modelState,
Func<RedirectToRoute...
Passing functions around
private void AddErrorIf<T>(Expression<Func<T>> fn,
ModelStateDictionary modelState,
Func<ModelStateDictionary,
Func<T,stri...
http://www.thegeekshowpodcast.com/home/mastashake/thegeekshowpodcast.com/wp-content/uploads/2009/07/wtf-cat.jpg
So what could possibly go wrong?
http://icanhascheezburger.files.wordpress.com/2009/06/funny-pictures-cat-does-not-think-p...
Hard to diagnose errors
var people = new []
{
new Person { Id=1, Address =
new Address { Road = "Ewloe Road" }},
new Person { Id=2},
new Person { ...
Null Reference Exception on line 23
http://www.flickr.com/photos/29599641@N04/3147972713/
public T Tap(T t, Action action)
{
action();
return t;
}
people
.Select(p => Tap(p, logger.debug(p.Id))
.Select(p => p.Address.Road);
if we have side effects then favour foreach construct
foreach(var item in items)
{
itemRepository.Save(item);
}
rather than:
items.ToList().ForEach(item => itemRepository.Save(item));
Readability
Lazy evaluation can have unexpected
consequences
IEnumerable<string> ReadNamesFromFile()
{
using(var fileStream = new FileStream("names.txt",
FileMode.Open))
using(var rea...
IEnumerable<Person> GetPeople()
{
return ReadNamesFromFile()
.Select(name => new Person(name));
}
IEnumerable<Person> people = GetPeople();
foreach (var person in people)
{
Console.WriteLine(person.Name);
}
Console.Write...
Encapsulation is still important
public Money CalculateSomething(Func<Customer,
DateTime, Money> calculation)
{
// do some calculation
}
public delegate Money PremiumCalculation(Customer
customer, DateTime renewalDate);
public Money CalculateSomething(
PremiumCalculation calculation)
{
// do some calculation
}
Total salary for a company
company.Employees
.Select(employee => employee.Salary)
.Sum()
This could lead to duplication
Wh...
Linq isn't the problem here, it's where we
have put it
Company naturally has the responsibility so
encapsulate the logic here
class Company
{
public int TotalSalary
{
get
{
return employees.Select(e =>e.Salary).Sum();
}
}
}
Sometimes we need to go further
If both Company and Division have employees do
we duplicate the logic for total salary?
IEnumerable<T> and List<T> make collections
easy but sometimes it is still better to create a
class to represent a collect...
class EmployeeCollection
{
private List<Employee> employees;
public int TotalSalary
{
get
{
return employees.Select(e => e...
In conclusion…
Mark Needham
mneedham@thoughtworks.com
© ThoughtWorks 2010
Mixing functional programming approaches in an object oriented language
Upcoming SlideShare
Loading in …5
×

Mixing functional programming approaches in an object oriented language

1,311 views

Published on

My slides from Biz Tech Delhi

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,311
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
30
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • So now we can change that function to read like this
  • origins of functional programming are found in lambda calculation/maths
  • origins of functional programming are found in lambda calculation/maths
  • functions that take in a function or return a function. Need to have first class functions in the language to do that. We have that with all the LINQ methods - select, where, and so on.
  • side effect free functions - input and output. ‘Nothing’ else should be affected

    We can't achieve this idiomatically in C# because the language isn't really designed for it.

    in this section?
  • the whole premise of functional programming with side effect free functions assumes that we have immutable data.

    We can't achieve this idiomatically in C# because the language isn't really designed for it.

    I want to put an example of how immutability is easy in F#, can that go in this section?
  • iterators in C# do this with yield keyword

    It's not necessary to have lazy evaluation to be functional but it's a characteristic of some functional languages.
  • seems quite obvious but the most extreme guideline to follow is that we shouldn't need to store anything in variables.

    Look at the data as a whole

    if we don't store any intermediate values then we truly do have some data that we are passing through different filters and applying some transformation
  • it's quite like the pipes and filters architectural pattern in fact.

    This is the way that we can combine functions on the unix command line.
  • what is CPS?  

    is where we pass in a function that represents the rest of the program which will be called with the result of another function.
  • A version of the maybe monad
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • what is CPS?  

    is where we pass in a function that represents the rest of the program which will be called with the result of another function.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • the idea is that the rest of the program is contained in the continuation so we don't need to come back to the call site.
  • Encapsulates the state but over complicates the program flow perhaps
  • what is CPS?  

    is where we pass in a function that represents the rest of the program which will be called with the result of another function.
  • Encapsulates the state but over complicates the program flow perhaps
  • Name the delegates if they’re used all over the place
  • Mixing functional programming approaches in an object oriented language

    1. 1. Applying functional programming approaches in object oriented languages Mark Needham © ThoughtWorks 2010
    2. 2. C# 1.0
    3. 3. int[] ints = new int[] {1, 2, 3, 4, 5}
    4. 4. int[] Filter(int[] ints) { ArrayList results = new ArrayList(); foreach (int i in ints) { if (i % 2 == 0) { results.Add(i); } } return results.ToArray(typeof(int)); }
    5. 5. int[] ints = new int[] {1, 2, 3, 4, 5}
    6. 6. int[] Filter(int[] ints) { ArrayList results = new ArrayList(); foreach (int i in ints) { if (i > 3 == 0) { results.Add(i); } } return results.ToArray(typeof(int)); }
    7. 7. int[] Filter(int[] ints) { ArrayList results = new ArrayList(); foreach (int i in ints) { if (i % 2 == 0) { results.Add(i); } } return results.ToArray(typeof(int)); }
    8. 8. int[] Filter(int[] ints) { ArrayList results = new ArrayList(); foreach (int i in ints) { if (i >3 == 0) { results.Add(i); } } return results.ToArray(typeof(int)); }
    9. 9. interface IIntegerPredicate { bool Matches(int value); }
    10. 10. class EvenPredicate : IIntegerPredicate { bool Matches(int value) { return value % 2 == 0; } }
    11. 11. class GreaterThan3Predicate : IIntegerPredicate { bool Matches(int value) { return value > 3; } }
    12. 12. int[] Filter(int[] ints, IIntegerPredicate predicate) { ArrayList results = new ArrayList(); foreach (int i in ints) { if (predicate.Matches(i)) { results.Add(i); } } return results.ToArray(typeof(int)); }
    13. 13. int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints, new EvenPredicate()); int[] greaterThan3 = Filter(ints, new GreaterThan3Predicate());
    14. 14. interface IIntegerPredicate { bool Matches(int value); }
    15. 15. bool delegate IntegerPredicate(int value);
    16. 16. bool Even(int value) { return value % 2 == 0; }
    17. 17. bool GreaterThan3(int value) { return value > 3; }
    18. 18. int[] Filter(int[] ints, IntegerPredicate predicate) { ArrayList results = new ArrayList(); foreach (int i in ints) { if (predicate(i)) { results.Add(i); } } return results.ToArray(typeof(int)); }
    19. 19. int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints, new IntegerPredicate(Even)); int[] greaterThan3 = Filter(ints, new IntegerPredicate(GreaterThan3));
    20. 20. C# 2.0
    21. 21. Inference int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints, new IntegerPredicate(Even)); int[] greaterThan3 = Filter(ints, new IntegerPredicate(GreaterThan3));
    22. 22. Inference int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints, Even); int[] greaterThan3 = Filter(ints, GreaterThan3);
    23. 23. Generics delegate bool IntegerPredicate(int value);
    24. 24. Generics delegate bool Predicate<T> (T value);
    25. 25. Generics int[] Filter(int[] ints, IntegerPredicate predicate) { ArrayList results = new ArrayList(); foreach (int i in ints) { if (predicate(i)) { results.Add(i); } } return results.ToArray(typeof(int)); }
    26. 26. Generics T[] Filter<T>(T[] values, Predicate<T> predicate) { List<T> results = new List<T>(); foreach (T i in value) { if (predicate(i)) { results.Add(i); } } return results.ToArray(); }
    27. 27. Iterators IEnumerable<T> Filter<T>(IEnumerable<T> values, Predicate<T> p) { List<T> results = new List<T>(); foreach (T i in value) { if (p(i)) { results.Add(i); } } return results; }
    28. 28. Iterators IEnumerable<T> Filter<T>(IEnumerable<T> values, Predicate<T> p) { foreach (T i in value) { if (p(i)) { yield return i; } } }
    29. 29. Anonymous Methods IEnumerable<int> greaterThan3 = Filter(ints, GreaterThan3);
    30. 30. Anonymous Methods IEnumerable<int> greaterThan3 = Filter(ints, delegate(int value) { return value > 3; });
    31. 31. Anonymous Methods int minimumValue = 3; IEnumerable<int> greaterThan3 = Filter(ints, delegate(int value) { return value > minimumValue; });
    32. 32. C# 3.0
    33. 33. Lambdas int minimumValue = 3; IEnumerable<int> greaterThan3 = Filter(ints, delegate(int value) { return value > minimumValue; });
    34. 34. Lambdas int minimumValue = 3; IEnumerable<int> greaterThan3 = Filter(ints, value => value > minimumValue);
    35. 35. More Type Inference int minimumValue = 3; IEnumerable<int> greaterThan3 = Filter(ints, value => value > minimumValue);
    36. 36. More Type Inference int minimumValue = 3; var greaterThan3 = Filter(ints, value => value > minimumValue);
    37. 37. Extension Methods int minimumValue = 3; var greaterThan3 = Filter(ints, value => value > minimumValue);
    38. 38. Extension Methods int minimumValue = 3; var greaterThan3 = ints.Filter(value => value > minimumValue);
    39. 39. LINQ
    40. 40. LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc.
    41. 41. LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc. System.Linq Extension methods Where, Select, OrderBy etc.
    42. 42. LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc. System.Linq Extension methods Where, Select, OrderBy etc. Some compiler magic to translate sql style code to method calls
    43. 43. LINQ var even = ints.Where(value => value % 2 == 0) var greaterThan3 = ints.Where(value => value > 3) or var even = from value in ints where value % 2 == 0 select value var greaterThan3 = from value in ints where value > 3 select value
    44. 44. Interfaces Delegates Anonymous methods Lambdas
    45. 45. So this functional programming thing…
    46. 46. http://www.flickr.com/photos/stuartpilbrow/2938100285/sizes/l/
    47. 47. Higher order functions
    48. 48. var ints = new int[] {1, 2, 3, 4, 5 }; var greaterThan3 = ints.Where(value => value > 3) var even = ints.Where(value => value % 2 == 0)
    49. 49. Pure functions
    50. 50. Immutability
    51. 51. Lazy evaluation
    52. 52. Iterators IEnumerable<T> Filter<T>(IEnumerable<T> values, Predicate<T> p) { foreach (T i in value) { if (p(i)) { yield return i; } } }
    53. 53. Recursion & Pattern Matching
    54. 54. Transformational Mindset We can just pass functions around instead in most cases - find an example where it still makes sense to use the GOF approach though.
    55. 55. Input -> ??? -> ??? -> ??? -> Output
    56. 56. http://www.emt-india.net/process/petrochemical/img/pp4.jpg
    57. 57. So why should you care?
    58. 58. Functional can fill in the gaps in OO code
    59. 59. Programming in the… Large Medium Small
    60. 60. Programming in the … Large Medium Small “a high level that affects as well as crosscuts multiple classes and functions”
    61. 61. Programming in the … Large Medium Small “a single API or group of related APIs in such things as classes, interfaces, modules”
    62. 62. Programming in the … Large Medium Small “individual function/method bodies”
    63. 63. Large Medium Small
    64. 64. Large Medium Small
    65. 65. Abstractions over common operations means less code and less chances to make mistakes
    66. 66. Projection
    67. 67. people.Select(person => person.Name)
    68. 68. people.SelectMany(person => person.Pets)
    69. 69. Restriction
    70. 70. people.Where(person => person.HasPets)
    71. 71. Partitioning
    72. 72. people.Take(5)
    73. 73. people.Skip(5)
    74. 74. people.TakeWhile(person => person.Name != "David")
    75. 75. people.SkipWhile(person => person.Name != "David")
    76. 76. Set
    77. 77. people.Select(person => person.Name) .Distinct()
    78. 78. people.Union(someOtherPeople)
    79. 79. people.Intersect(someOtherPeople)
    80. 80. people.Except(someOtherPeople)
    81. 81. Ordering and Grouping
    82. 82. people.OrderBy(person => person.Name)
    83. 83. people.GroupBy(person => person.Name)
    84. 84. Aggregation
    85. 85. people.Count()
    86. 86. people.Select(person => person.Age) .Sum()
    87. 87. people.Select(person => person.Age) .Min()
    88. 88. people.Select(person => person.Age) .Max()
    89. 89. people.Select(person => person.Age) .Average()
    90. 90. people.Select(person => person.Age) .Aggregate(0, (totalAge, nextAge) => nextAge % 2 == 0 ? nextAge + totalAge : totalAge)
    91. 91. Joining
    92. 92. people.Join(addresses, person => person.PersonId, address => address.PersonId, (person, address) => new { person, address})
    93. 93. Embrace the collection
    94. 94. var first = “Mark”; var middle = “Harold”; var surname = “Needham”; var fullname = first + “ “ + middle + “ “ + surname; or var names = new[] {first, middle, surname}; var fullname = String.Join(“ “, names);
    95. 95. public class SomeObject { public SomeObject(string p1, string p2, string p3) { if(p1 == null) throw new Exception(…); if(p2 == null) throw new Exception(…); if(p3 == null) throw new Exception(…); // rest of constructor logic } }
    96. 96. public class SomeObject { public SomeObject(string p1, string p2, string p3) { var parameters = new[] {p1, p2, p3}; if(parameters.Any(p => p = null) throw new Exception(…). // rest of constructor logic } }
    97. 97. Large Medium Small
    98. 98. We can just pass functions around instead in most cases - find an example where it still makes sense to use the GOF approach though.
    99. 99. public class SomeObject { private readonly IStrategy strategy; public SomeObject(IStrategy strategy) { this.strategy = strategy; } public void DoSomething(string value) { strategy.DoSomething(value); } }
    100. 100. public class Strategy : IStrategy { public void DoSomething(string value) { // do something with string } }
    101. 101. public class SomeObject { private readonly Action<string> strategy; public SomeObject(Action<string> strategy) { this.strategy = strategy; } public void DoSomething(string value) { strategy(value); } }
    102. 102. Hole in the middle pattern
    103. 103. public class ServiceCache<Service> { protected Res FromCacheOrService <Req, Res>(Func<Res> serviceCall, Req request) { var cachedRes = cache.RetrieveIfExists( typeof(Service), typeof(Res), request); if(cachedRes == null) { cachedRes = serviceCall(); cache.Add(typeof(Service), request, cachedRes); } return (Res) cachedRes; } }
    104. 104. public class CachedService : ServiceCache<IService> { public MyResult GetMyResult(MyRequest request) { return FromCacheOrService(() => service.GetMyResult(request), request); } }
    105. 105. Maybe?
    106. 106. public interface Maybe<T> { bool HasValue(); T Value(); }
    107. 107. public class Some<T> : Maybe<T> { private readonly T theThing; public Some(T theThing) { this.theThing = theThing; } public bool HasValue () { return true; } public T Value() { return theThing; } }
    108. 108. public class None<T> : Maybe<T> { public bool HasValue () { return false; } public T Value() { throw new NotImplementedException(); } }
    109. 109. public class Some { public static Some<T> Thing<T>(T thing) : where T : class { return new Some<T>(thing); } }
    110. 110. public class No { public static None<T> Thing<T>() { return new None<T>(); } }
    111. 111. public static class MaybeExtensions { public static Maybe<T> Maybify<T>(this T source) where T : class { if(source == null) return No.Thing<T>(); return Some.Thing(source); } } recordFromDatabase.Maybify():
    112. 112. public class FooService { public Foo FindOrCreate(int fooId) { var foo = fooRepository.Find(fooId); if(foo.HasValue()) { return foo.Value(); } return fooRepository.Create(fooId); } }
    113. 113. Continuation Passing Style
    114. 114. static void Identity<T>(T value, Action<T> k) { k(value); }
    115. 115. Identity("foo", s => Console.WriteLine(s));
    116. 116. Identity("foo", s => Console.WriteLine(s)); as compared to var foo = Identity(“foo”); Console.WriteLine(foo);
    117. 117. public ActionResult Submit(string id, FormCollection form) { var shoppingBasket = CreateShoppingBasketFrom(id, form); return IsValid(shoppingBasket, ModelState, () => RedirectToAction("index", "ShoppingBasket", new { shoppingBasket.Id} ), () => LoginUser(shoppingBasket, () => { ModelState.AddModelError("Password", "User name/email address was incorrect - please re-enter"); return RedirectToAction("index", ""ShoppingBasket", new { Id = new Guid(id) }); }, user => { shoppingBasket.User = user; UpdateShoppingBasket(shoppingBasket); return RedirectToAction("index", "Purchase", new { Id = shoppingBasket.Id }); } )); }
    118. 118. private RedirectToRouteResult IsValid(ShoppingBasket shoppingBasket, ModelStateDictionary modelState, Func<RedirectToRouteResult> failureFn, Func<RedirectToRouteResult> successFn) { return validator.IsValid(shoppingBasket, modelState) ? successFn() : failureFn(); } private RedirectToRouteResult LoginUser(ShoppingBasket shoppingBasket, Func<RedirectToRouteResult> failureFn, Func<User,RedirectToRouteResult> successFn) { User user = null; try { user = userService.CreateAccountOrLogIn(shoppingBasket); } catch (NoAccountException) { return failureFn(); } return successFn(user); }
    119. 119. Passing functions around
    120. 120. private void AddErrorIf<T>(Expression<Func<T>> fn, ModelStateDictionary modelState, Func<ModelStateDictionary, Func<T,string, string, bool>> checkFn) { var fieldName = ((MemberExpression)fn.Body).Member.Name; var value = fn.Compile().Invoke(); var validationMessage = validationMessages[fieldName]); checkFn.Invoke(modelState)(value, fieldName, validationMessage); } AddErrorIf(() => person.HasPets, modelState, m => (v, f, e) => m.AddErrorIfNotEqualTo(v,true, f, e)); AddErrorIf(() => person.HasChildren, modelState, m => (v, f, e) => m.AddErrorIfNull(v, f, e));
    121. 121. http://www.thegeekshowpodcast.com/home/mastashake/thegeekshowpodcast.com/wp-content/uploads/2009/07/wtf-cat.jpg
    122. 122. So what could possibly go wrong? http://icanhascheezburger.files.wordpress.com/2009/06/funny-pictures-cat-does-not-think-plan-will-fail.jpg
    123. 123. Hard to diagnose errors
    124. 124. var people = new [] { new Person { Id=1, Address = new Address { Road = "Ewloe Road" }}, new Person { Id=2}, new Person { Id=3, Address = new Address { Road = "London Road"}} }; people.Select(p => p.Address.Road);
    125. 125. Null Reference Exception on line 23
    126. 126. http://www.flickr.com/photos/29599641@N04/3147972713/
    127. 127. public T Tap(T t, Action action) { action(); return t; }
    128. 128. people .Select(p => Tap(p, logger.debug(p.Id)) .Select(p => p.Address.Road);
    129. 129. if we have side effects then favour foreach construct foreach(var item in items) { itemRepository.Save(item); }
    130. 130. rather than: items.ToList().ForEach(item => itemRepository.Save(item));
    131. 131. Readability
    132. 132. Lazy evaluation can have unexpected consequences
    133. 133. IEnumerable<string> ReadNamesFromFile() { using(var fileStream = new FileStream("names.txt", FileMode.Open)) using(var reader = new StreamReader(fileStream)) { var nextLine = reader.ReadLine(); while(nextLine != null) { yield return nextLine; nextLine = reader.ReadLine(); } } }
    134. 134. IEnumerable<Person> GetPeople() { return ReadNamesFromFile() .Select(name => new Person(name)); }
    135. 135. IEnumerable<Person> people = GetPeople(); foreach (var person in people) { Console.WriteLine(person.Name); } Console.WriteLine("Total number of people: " + people.Count());
    136. 136. Encapsulation is still important
    137. 137. public Money CalculateSomething(Func<Customer, DateTime, Money> calculation) { // do some calculation }
    138. 138. public delegate Money PremiumCalculation(Customer customer, DateTime renewalDate);
    139. 139. public Money CalculateSomething( PremiumCalculation calculation) { // do some calculation }
    140. 140. Total salary for a company company.Employees .Select(employee => employee.Salary) .Sum() This could lead to duplication What if we add rules to the calculation? Who should really have this responsibility? .Sum()
    141. 141. Linq isn't the problem here, it's where we have put it
    142. 142. Company naturally has the responsibility so encapsulate the logic here
    143. 143. class Company { public int TotalSalary { get { return employees.Select(e =>e.Salary).Sum(); } } }
    144. 144. Sometimes we need to go further
    145. 145. If both Company and Division have employees do we duplicate the logic for total salary?
    146. 146. IEnumerable<T> and List<T> make collections easy but sometimes it is still better to create a class to represent a collection
    147. 147. class EmployeeCollection { private List<Employee> employees; public int TotalSalary { get { return employees.Select(e => e.Salary).Sum(); } } }
    148. 148. In conclusion…
    149. 149. Mark Needham mneedham@thoughtworks.com © ThoughtWorks 2010

    ×