Skip to content

Instantly share code, notes, and snippets.

@beyond-code-github
Last active January 26, 2017 18:26
Show Gist options
  • Save beyond-code-github/8711794c4d516cb6941d47274884b248 to your computer and use it in GitHub Desktop.
Save beyond-code-github/8711794c4d516cb6941d47274884b248 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
namespace OnlineShop
{
delegate decimal Discount(decimal amount, int yearsAccountHeld);
public static class Discounts
{
public static decimal NotRegistered(decimal amount, int yearsAccountHeld) => amount;
public static decimal SimpleCustomer(decimal amount, int yearsAccountHeld) =>
(amount * 0.9m) * LoyaltyDiscount(yearsAccountHeld);
public static decimal ValuableCustomer(decimal amount, int yearsAccountHeld) =>
(amount * 0.7m) * LoyaltyDiscount(yearsAccountHeld);
public static decimal MostValuableCustomer(decimal amount, int yearsAccountHeld) =>
(amount * 0.5m) * LoyaltyDiscount(yearsAccountHeld);
private static decimal LoyaltyDiscount(int yearsAccountHeld) => 1 - (Math.Min(yearsAccountHeld, 5) / 100);
}
public class DiscountCalculator
{
private Dictionary<int, Discount> applyDiscountFor = new Dictionary<int, Discount>()
{
{ 0, Discounts.NotRegistered },
{ 1, Discounts.SimpleCustomer },
{ 2, Discounts.ValuableCustomer },
{ 3, Discounts.MostValuableCustomer }
};
public decimal Calculate(decimal amount, int accountType, int yearsAccountHeld) =>
this.applyDiscountFor[accountType](amount, yearsAccountHeld);
}
}
@nosami
Copy link

nosami commented Jan 25, 2017

straight port to F#... not as good as the original F# version

open System
module Calculator =
    let loyaltyDiscount yearsAccountHeld = 1.0m - Math.Min(yearsAccountHeld, 5.0m) / 100.0m
    let notRegistered amount yearsAccountHeld = amount
    let simpleCustomer amount yearsAccountHeld = (amount * 0.9m) * loyaltyDiscount yearsAccountHeld
    let valuableCustomer amount yearsAccountHeld = (amount * 0.7m) * loyaltyDiscount yearsAccountHeld
    let mostValuableCustomer amount yearsAccountHeld = (amount * 0.5m) * loyaltyDiscount yearsAccountHeld
    let map = [ 0, notRegistered; 1, simpleCustomer; 2, valuableCustomer; 3, mostValuableCustomer ] |> dict
    let calculate amount yearsAccountHeld accountType = map.[accountType] amount yearsAccountHeld

@nosami
Copy link

nosami commented Jan 26, 2017

Weirdest part about this code for me (besides int accountType) is that NotRegistered has an unused yearsAccountHeld parameter just to make it fit the other method signatures.

You can imagine this approach becoming much worse as the problem domain increases in complexity.

@mcintyre321
Copy link

Nice rewrite!

You can add a sprinkling of type inference to the rule declarations by declaring them as Discount properties e.g. static Discount SimpleCustomer = (amount, yearsAccountHeld) => (amount * 0.9m) * LoyaltyDiscount(yearsAccountHeld);

@smoothdeveloper
Copy link

smoothdeveloper commented Jan 26, 2017

The F# (non idiomatic) translation:

type YearsAccountHeld = int
type DiscountAmount = decimal
type Discount = DiscountAmount -> YearsAccountHeld -> decimal
module Discounts =
  let inline loyaltyDiscount yearsAccountHeld = 1m - ((min (decimal yearsAccountHeld) 5m) / 100m)
  let inline makeDiscount discount (amount: DiscountAmount) (yearsAccountHeld: YearsAccountHeld) =
    (amount * discount) * (loyaltyDiscount yearsAccountHeld)
  let NotRegistered amount _ = amount
  let ValuableCustomer       = makeDiscount 0.7m
  let MostValuableCustomer   = makeDiscount 0.5m
  let SimpleCustomer         = makeDiscount 0.9m

module DiscountCalculator =
  let applyDiscountFor = 
    dict [
      (0, Discounts.NotRegistered)
      (1, Discounts.SimpleCustomer)
      (2, Discounts.ValuableCustomer)
      (3, Discounts.MostValuableCustomer)
    ]
  let Calculate amount accountType yearsAccountHeld = applyDiscountFor.[accountType] amount yearsAccountHeld

// try it right away in FSI
DiscountCalculator.Calculate 100m 0 100 // 100m
DiscountCalculator.Calculate 100m 1 100 // 85.5m
DiscountCalculator.Calculate 100m 2 100 // 66.5m
DiscountCalculator.Calculate 100m 3 100 // 47.5m

Not a vast improvement on the C# but remove most syntactical / keyword cluter

The sum types solution that started this discussion is of course better

For those wondering what's up here

https://www.codeproject.com/articles/1083348/csharp-bad-practices-learn-how-to-make-a-good-code
http://functionalsoftware.net/fsharp-rewrite-of-a-fully-refactored-csharp-clean-code-example-612/
and now this gist and https://twitter.com/beyond_code/status/824367640298844162

😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment