applying solid principles

47

Upload: mikeabney

Post on 13-Jul-2015

329 views

Category:

Technology


1 download

TRANSCRIPT

OO SOLIDApplying SOLID principles to improve object-oriented designs

Say we have a mechanic shop...

ServiceOrder

AddPart(Part)

Total()

Part

Name

Price

1..*

! public class Part { private decimal price; public Part(string name, decimal price) { this.price = price; } public decimal Price { get { return price; } set { price = value; } } }

! public class ServiceOrder { private List<Part> parts; public ServiceOrder() { parts = new List<Part>(); } public void AddPart(Part part) { parts.Add(part); } public decimal Total() { decimal total = 0; foreach (Part aPart in parts) { total += aPart.Price; } return total; } }

Now we want to specify additional quantities of each part. (because we are lazy)

ServiceOrder

partsAndQtys : Dictionary

AddPart(Part)

Total()

Part

Name

Price

1..*

! public class ServiceOrder { private Dictionary<Part, int> partsAndQuantities; public ServiceOrder() { partsAndQuantities = new Dictionary<Part, int>(); } public void AddPart(Part part) { this.AddPart(part, 1); } public void AddPart(Part part, int quantity) { int currentQuantity = 0; if (partsAndQuantities.ContainsKey(part)) { currentQuantity = partsAndQuantities[part]; } int newQuantity = currentQuantity + quantity; partsAndQuantities.Add(part, newQuantity); } public decimal Total() { decimal total = 0; foreach (Part aPart in partsAndQuantities.Keys) { int quantity = partsAndQuantities[aPart]; total += aPart.Price * quantity; } return total; } }

Part

Name

Price

1..*

ServiceOrder

AddPart(Part, Qty)

Total()

LineItem

Part

Quantity

1

1*

! public class LineItem { private Part part; private int quantity; public LineItem(Part part, int quantity) { this.part = part; this.quantity = quantity; } public Part Part { get { return part; } } public int Quantity { get { return quantity; } private set { quantity = value; } } }

public class ServiceOrder { private List<LineItem> lineItems; public ServiceOrder() { lineItems = new List<LineItem>(); } public void AddPart(Part part) { this.AddPart(part, 1); } public void AddPart(Part part, int quantity) { LineItem lineItem = new LineItem(part, quantity); lineItems.Add(lineItem); } public decimal Total() { decimal total = 0; foreach (LineItem aLineItem in lineItems) { total += aLineItem.Part.Price * aLineItem.Quantity; } return total; } }

Single Responsibility Principle

Now we want to add a markup for each part.

(because we want to track profit margin)

Part

Name

MarkupPercentage

WholesalePrice

1..*

ServiceOrder

AddPart(Part, Qty)

Total()

1

1*

LineItem

Part

Quantity

! public class Part { private string name; private decimal wholesalePrice; private int markupPercentage; public Part(string name, decimal price) :this(name, price, 0) {} public Part(string name, decimal price, int markupPercentage) { this.name = name; this.wholesalePrice = price; this.markupPercentage = markupPercentage; } public string Name { get { return name; } } public decimal WholesalePrice { get { return wholesalePrice; } } public int MarkupPercentage { get { return markupPercentage; } } }

! public class ServiceOrder { private List<LineItem> lineItems; public ServiceOrder() { lineItems = new List<LineItem>(); } public void AddPart(Part part) { this.AddPart(part, 1); } public void AddPart(Part part, int additionalQuantity) { LineItem lineItem = new LineItem(part, additionalQuantity); lineItems.Add(lineItem); } public decimal Total() { decimal total = 0; foreach (LineItem aLineItem in lineItems) { total += aLineItem.Part.WholesalePrice * (aLineItem.Part.MarkupPercentage/100m + 1) * aLineItem.Quantity; } return total; } }

Part

Name

MarkupPercentage

WholesalePrice

1..*

ServiceOrder

AddPart(Part, Qty)

Total()

1

1*

LineItem

Part

Quantity

LineTotal()

! // New methods in the Part class public decimal Price { get { return wholesalePrice * MarkupMultiplier(); } } ! private decimal MarkupMultiplier() { return (markupPercentage / 100m + 1); }!! // And one in the LineItem class public decimal LineTotal() { return part.Price * quantity; }

! // In the ServiceOrder class... public decimal Total() { decimal total = 0; foreach (LineItem aLineItem in lineItems) { total += aLineItem.LineTotal(); } return total; }

Open/Closed Principle

Now we want to add labor costs. (because we want to make more money)

Part

Name

MarkupPercentage

WholesalePrice

Price()

Part

Name

MarkupPercentage

WholesalePrice

Labor

Price

Price()

! public class Labor : Part { public Labor(string name, decimal price) : base(name, price) {} }

Part

MarkupPercentage

WholesalePrice

CalculatePrice()

Labor

Price

CalculatePrice()

Item

Name

CalculatePrice()

! public interface Item { string Name { get; } decimal CalculatePrice(); }

Liskov Substitution Principle

Now we want to generate receipts.

(because we want to seem professionalish)

ReceiptGenerator

PrintReceipt()

ServiceOrder

AddPart(Part, Qty)

Total()

Presentation Layer Business Layer

! public class ReceiptGenerator { private FixItMechanicsSystem system; private EpsonTMT88IVPrinter epsonTMT88IVPrinter; private string initString = "sasllkslsl398383fa"; public void PrintReceipt() { ServiceOrder order = system.FinalizeOrder(); string receiptString = "Total........ $" + order.Total(); epsonTMT88IVPrinter.Initialize(initString); epsonTMT88IVPrinter.PrintBwMed239(receiptString); epsonTMT88IVPrinter.Flush(); } }

ReceiptGenerator

PrintReceipt()

ServiceOrder

AddPart(Part, Qty)

Total()

Presentation Layer Business Layer

Receipt

Total()

// In the ReciptGenerator class. public void PrintReceipt() { IReceipt receipt = system.FinalizeOrder(); string receiptString = "Total........ $" + receipt.Total(); epsonTMT88IVPrinter.Initialize(initString); epsonTMT88IVPrinter.PrintBwMed239(receiptString); epsonTMT88IVPrinter.Flush(); }

Interface Segregation Principle

We need to print to a new printer.

(because we’re growing and they don’t make the old one anymore)

EpsonT88IVPrinter

Initialize(String)

PrintBwMed239(String)

Flush()

1ReceiptGenerator

PrintReceipt()

! public class ReceiptGenerator { private FixItMechanicsSystem system; private EpsonTMT88IV epsonTMT88IVPrinter; private string initString = "sasllkslsl398383fa"; public void PrintReceipt() { Receipt receipt = system.FinalizeOrder(); string receiptString = "Total........ $" + receipt.Total(); epsonTMT88IVPrinter.Initialize(initString); epsonTMT88IVPrinter.PrintBwMed239(receiptString); epsonTMT88IVPrinter.Flush(); } }

EpsonT88IVPrinter

Initialize(String)

PrintBwMed239(String)

Flush()

1ReceiptGenerator

PrintReceipt()

GenericPrinter

Print(String)

SamsungSRP350

Initialize(String)

ProcessPages(String)

EpsonPrinterAdapter SamsungPrinterAdapter

11

public class ReceiptGenerator { private FixItMechanicsSystem system; private IGenericPrinter printer; public void PrintReceipt() { Receipt receipt = system.FinalizeOrder(); string receiptString = "Total........ $" + receipt.Total(); printer.Print(receiptString); } }

public class EpsonPrinterAdapter : IGenericPrinter { private EpsonTMT88IV epsonTMT88IVPrinter; private string initString = "sasllkslsl398383fa"; public void Print(string printString) { epsonTMT88IVPrinter.Initialize(initString); epsonTMT88IVPrinter.PrintBwMed239(printString); epsonTMT88IVPrinter.Flush(); } }

public class SamsungPrinterAdapter : IGenericPrinter { private SamsungSRP350 samsungSRP350Printer; private string initString = "aksreiufgu"; public void Print(string printString) { samsungSRP350Printer.Init(initString); samsungSRP350Printer.ProcessPages(printString); } }

Dependency Inversion Principle

Single Responsibility Principle

Open/Closed Principle

Liskov Substitution Principle

Interface Segregation Principle

Dependency Inversion Principle

Questions?

• Duplication

• Ambiguity

• Complexity XPoor Design

✓Good Design

✓ Low Coupling

✓ High Cohesion

Questions?