Advertisement
Guest User

Untitled

a guest
Apr 23rd, 2017
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.21 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4.  
  5. namespace WhyUseFsharp
  6. {
  7.  
  8. public class ShoppingCart<TItem>
  9. {
  10.  
  11. #region ShoppingCart State classes
  12.  
  13. /// <summary>
  14. /// Represents the Empty state
  15. /// </summary>
  16. public class EmptyState
  17. {
  18. public ShoppingCart<TItem> Add(TItem item)
  19. {
  20. var newItems = new[] { item };
  21. var newState = new ActiveState(newItems);
  22. return FromState(newState);
  23. }
  24. }
  25.  
  26. /// <summary>
  27. /// Represents the Active state
  28. /// </summary>
  29. public class ActiveState
  30. {
  31. public ActiveState(IEnumerable<TItem> items)
  32. {
  33. Items = items;
  34. }
  35.  
  36. public IEnumerable<TItem> Items { get; private set; }
  37.  
  38. public ShoppingCart<TItem> Add(TItem item)
  39. {
  40. var newItems = new List<TItem>(Items) {item};
  41. var newState = new ActiveState(newItems);
  42. return FromState(newState);
  43. }
  44.  
  45. public ShoppingCart<TItem> Remove(TItem item)
  46. {
  47. var newItems = new List<TItem>(Items);
  48. newItems.Remove(item);
  49. if (newItems.Count > 0)
  50. {
  51. var newState = new ActiveState(newItems);
  52. return FromState(newState);
  53. }
  54. else
  55. {
  56. var newState = new EmptyState();
  57. return FromState(newState);
  58. }
  59. }
  60.  
  61. public ShoppingCart<TItem> Pay(decimal amount)
  62. {
  63. var newState = new PaidForState(Items, amount);
  64. return FromState(newState);
  65. }
  66.  
  67.  
  68. }
  69.  
  70. /// <summary>
  71. /// Represents the Paid state
  72. /// </summary>
  73. public class PaidForState
  74. {
  75. public PaidForState(IEnumerable<TItem> items, decimal amount)
  76. {
  77. Items = items.ToList();
  78. Amount = amount;
  79. }
  80.  
  81. public IEnumerable<TItem> Items { get; private set; }
  82. public decimal Amount { get; private set; }
  83. }
  84.  
  85. #endregion ShoppingCart State classes
  86.  
  87. //====================================
  88. // Execute of shopping cart proper
  89. //====================================
  90.  
  91. private enum Tag { Empty, Active, PaidFor }
  92. private readonly Tag _tag = Tag.Empty;
  93. private readonly object _state; //has to be a generic object
  94.  
  95. /// <summary>
  96. /// Private ctor. Use FromState instead
  97. /// </summary>
  98. private ShoppingCart(Tag tagValue, object state)
  99. {
  100. _state = state;
  101. _tag = tagValue;
  102. }
  103.  
  104. public static ShoppingCart<TItem> FromState(EmptyState state)
  105. {
  106. return new ShoppingCart<TItem>(Tag.Empty, state);
  107. }
  108.  
  109. public static ShoppingCart<TItem> FromState(ActiveState state)
  110. {
  111. return new ShoppingCart<TItem>(Tag.Active, state);
  112. }
  113.  
  114. public static ShoppingCart<TItem> FromState(PaidForState state)
  115. {
  116. return new ShoppingCart<TItem>(Tag.PaidFor, state);
  117. }
  118.  
  119. /// <summary>
  120. /// Create a new empty cart
  121. /// </summary>
  122. public static ShoppingCart<TItem> NewCart()
  123. {
  124. var newState = new EmptyState();
  125. return FromState(newState);
  126. }
  127.  
  128. /// <summary>
  129. /// Call a function for each case of the state
  130. /// </summary>
  131. /// <remarks>
  132. /// Forcing the caller to pass a function for each possible case means that all cases are handled at all times.
  133. /// </remarks>
  134. public TResult Do<TResult>(
  135. Func<EmptyState, TResult> emptyFn,
  136. Func<ActiveState, TResult> activeFn,
  137. Func<PaidForState, TResult> paidForyFn
  138. )
  139. {
  140. switch (_tag)
  141. {
  142. case Tag.Empty:
  143. return emptyFn(_state as EmptyState);
  144. case Tag.Active:
  145. return activeFn(_state as ActiveState);
  146. case Tag.PaidFor:
  147. return paidForyFn(_state as PaidForState);
  148. default:
  149. throw new InvalidOperationException(string.Format("Tag {0} not recognized", _tag));
  150. }
  151. }
  152.  
  153. /// <summary>
  154. /// Do an action without a return value
  155. /// </summary>
  156. public void Do(
  157. Action<EmptyState> emptyFn,
  158. Action<ActiveState> activeFn,
  159. Action<PaidForState> paidForyFn
  160. )
  161. {
  162. //convert the Actions into Funcs by returning a dummy value
  163. Do(
  164. state => { emptyFn(state); return 0; },
  165. state => { activeFn(state); return 0; },
  166. state => { paidForyFn(state); return 0; }
  167. );
  168. }
  169.  
  170.  
  171.  
  172. }
  173.  
  174. /// <summary>
  175. /// Extension methods for my own personal library
  176. /// </summary>
  177. public static class ShoppingCartExtension
  178. {
  179. /// <summary>
  180. /// Helper method to Add
  181. /// </summary>
  182. public static ShoppingCart<TItem> Add<TItem>(this ShoppingCart<TItem> cart, TItem item)
  183. {
  184. return cart.Do(
  185. state => state.Add(item), //empty case
  186. state => state.Add(item), //active case
  187. state => { Console.WriteLine("ERROR: The cart is paid for and items cannot be added"); return cart; } //paid for case
  188. );
  189. }
  190.  
  191. /// <summary>
  192. /// Helper method to Remove
  193. /// </summary>
  194. public static ShoppingCart<TItem> Remove<TItem>(this ShoppingCart<TItem> cart, TItem item)
  195. {
  196. return cart.Do(
  197. state => { Console.WriteLine("ERROR: The cart is empty and items cannot be removed"); return cart; }, //empty case
  198. state => state.Remove(item), //active case
  199. state => { Console.WriteLine("ERROR: The cart is paid for and items cannot be removed"); return cart; } //paid for case
  200. );
  201. }
  202.  
  203. /// <summary>
  204. /// Helper method to Display
  205. /// </summary>
  206. public static void Display<TItem>(this ShoppingCart<TItem> cart)
  207. {
  208. cart.Do(
  209. state => Console.WriteLine("The cart is empty"),
  210. state => Console.WriteLine("The active cart contains {0} items", state.Items.Count()),
  211. state => Console.WriteLine("The paid cart contains {0} items. Amount paid {1}", state.Items.Count(), state.Amount)
  212. );
  213. }
  214. }
  215.  
  216. [NUnit.Framework.TestFixture]
  217. public class CorrectShoppingCartTest
  218. {
  219. [NUnit.Framework.Test]
  220. public void TestCart()
  221. {
  222. var emptyCart = ShoppingCart<string>.NewCart();
  223. emptyCart.Display();
  224.  
  225. var cartA = emptyCart.Add("A"); //one item
  226. cartA.Display();
  227.  
  228. var cartAb = cartA.Add("B"); //two items
  229. cartAb.Display();
  230.  
  231. var cartB = cartAb.Remove("A"); //one item
  232. cartB.Display();
  233.  
  234. var emptyCart2 = cartB.Remove("B"); //empty
  235. emptyCart2.Display();
  236.  
  237. Console.WriteLine("Removing from emptyCart");
  238. emptyCart.Remove("B"); //error
  239.  
  240.  
  241. // try to pay for cartA
  242. Console.WriteLine("paying for cartA");
  243. var paidCart = cartA.Do(
  244. state => cartA,
  245. state => state.Pay(100),
  246. state => cartA);
  247. paidCart.Display();
  248.  
  249. Console.WriteLine("Adding to paidCart");
  250. paidCart.Add("C");
  251.  
  252. // try to pay for emptyCart
  253. Console.WriteLine("paying for emptyCart");
  254. var emptyCartPaid = emptyCart.Do(
  255. state => emptyCart,
  256. state => state.Pay(100),
  257. state => emptyCart);
  258. emptyCartPaid.Display();
  259. }
  260. }
  261. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement