Delegates
Loosely coupling to delegates requires that another class (usually the other class) inform the class of what delegates to call. This is generally only useful if there are only one or two delegates. Use of another class that contains many methods that are used by the class becomes problematic to loosen coupling by using delegates simply due to the number of delegate initializations that need to occur.
Use of delegates is generally called callbacks. A callback is something that another class calls in order to obtain values or functionality from an external source. If you find that use of the other class requires values or functionality from the other class, use of callbacks may be an appropriate solution. This is particularly true if this is a comprehensive value or a single functionality.
In our Invoice class, we really only have one method that we need to inject into an Invoice object. This may be a perfect scenario for a callback. Refactoring to a callback is much the same as refactoring to interface-based design. In our particular case, we begin by accepting a Func<float, float, float> parameter. Then, add a Func<float, float, float> field named calculateGrandTotalCallback to the Invoice class. Next we need to initialize the calculateGrandTotalCallback field in the constructor. Finally, we need to replace the call to CalculateGrandTotal to an invocation of the calculateGrandTotalCallback field. The refactoring should result in something similar to the following:
/// <summary>/// Example of using callback/// instead of interface/// </summary>public class Invoice{ private Func<float, float, float> calculateGrandTotalCallback; public List<InvoiceLineItem> InvoiceLineItems { get; set; } public Invoice(IEnumerable<InvoiceLineItem> invoiceLineItems, Func<float, float, float> calculateGrandTotalCallback) { InvoiceLineItems = new List<InvoiceLineItem>(invoiceLineItems); this.calculateGrandTotalCallback = calculateGrandTotalCallback; } public float CalculateGrandTotal(float invoiceSubTotal, float invoiceTotalTax) { return calculateGrandTotalCallback(invoiceSubTotal, invoiceTotalTax); } //...}
If you find that you are only passing data to the other class and not receiving any data from it, then events are the best way of refactoring.