শুক্রবার, ১৫ নভেম্বর, ২০১৩

Liskovs Substitution Principle (LSP)


·         Subclass mg~n base class Gi substitutable n‡e|
·         Subclass mg~n‡K base Gi substitute wnmv‡e e¨envi Kiv hv‡e



Real World Comparison



A father is a real estate business man whereas his son wants to be cricketer.
A son can’t replace his father in spite of the fact that they belong to same family hierarchy. 



Identify Problem in Programming

·         Normally Avgiv hLb geometric shapes wb‡q KvR Kwi Avgiv rectangle ‡K square Gi base class wnmv‡e call Kwi|

·         GLb Avgiv Dnvi code ‡`wL-
using System;

/// <summary>
/// Summary description for Rectangle
/// </summary>
public class Rectangle
{
    public int Width { get; set; }
    public int Height { get; set; }
}

using System;

/// <summary>
/// Summary description for Square
/// </summary>
public class Square : Rectangle
{
    //codes specific to
    //square will be added
}
·         One can say,
·         Avgiv mvaviYZ: Rectangle class ‡K wb‡¤œv³fv‡e initialize K‡i _vwK
using System;


/// <summary>
/// Summary description for Initialization
/// </summary>
public class Initialization
{
    public void initializeRectangle()
    {
        Rectangle o = new Rectangle();
        o.Width = 5;
        o.Height = 6;
    }
}


·         GLb LSP Abyhvqx Avgiv Rectangle ‡K square Øviv replace Ki‡ev wb¤œiƒ‡c-
using System;


/// <summary>
/// Summary description for Initialization
/// </summary>
public class Initialization
{

    public void initializeRectangle()
    {
        Rectangle o = new Square();
        o.Width = 5;
        o.Height = 6;

    }
}
·         What is the matter?
o   Square cannot have different width and height.
·         What it means?
o   It means we can’t replace base with derived. Means we are violating LSP.
·         Why don’t we make width and height virtual in Rectangle, and override them in Square?
using System;

public class Square : Rectangle
{
    public override int Width
    {
        get { return base.Width; }
        set
        {
            base.Height = value;
            base.Width = value;
        }
    }
    public override int Height
    {
        get { return base.Height; }
        set
        {
            base.Height = value;
            base.Width = value;
        }
    }
}
·         We can’t because doing so we are violating LSP, as we are changing the behavior of Width and Height properties in derived class (for Rectangle height and width cannot be equal, if they are equal it’s cannot be Rectangle).
·         Avgiv Dc‡iv³fv‡e Ki‡Z cvi‡ev bv KviY Avgiv LSP violate KiwQ

Solution which will not violate LSP

·         cÖ_‡g LSP clearly demonstrate Kivi Rb¨ Avgiv KwZcq code ‡`L‡ev hvnviv LSP violate K‡i|
·         Then Avgiv principle G refactor Ki‡ev|
·         GB exercise Gi gva¨‡g principle following Gi myweav Ges ignored Gi problems Avgiv Rvb‡Z cvi‡ev|
·         Figure 5-19 shows the classes involved in this exercise.
·         bx‡Pi Figure G Avgiv exercise Gi mv‡_ involved class mg~n †`Lv‡bv n‡jv|
·         GLv‡b Avgiv e-commerce company Gi refund module wb‡q Av‡jvPbv Ki‡ev|
·         Specifically, the organization takes and refunds payments using the PayPal and WorldPay payment merchants via their respective web services— in this example, you will simply mock out these web services.








·         The RefundService class coordinates the refunding of a RefundRequest by first obtaining the correct payment class (WorldPayPayment or PayPalPayment) via the PaymentServiceFactory.
·         RefundService class GKwU RefundRequest Gi refunding coordinate K‡i
o    GRb¨ cÖ_‡g PaymentServiceFactory class Gi gva¨‡g WorldPayPayment A_ev PayPalPayment class G obtain Ki‡ev|
o    Then RefundRequest Gi refunding coordinate K‡i
·          Refund  nIqvi c‡i RefundResponse object Gi g‡a¨ transaction status wrapped n‡e Ges  client Gi Kv‡Q returned nq|
·         cÖ_‡g Avgiv GKwU enumeration create Kwi Ges Dnvi bvg w`B PaymentType Ges code listing wb¤œiæc-
using System;

public enum PaymentType
{
    PayPal = 1,
    WorldPay = 2
}
·         Next Avgiv RefundRequest I RefundResponse classes add Ki‡ev-


using System;
public class RefundRequest
{
    public PaymentType Payment { get; set; }
    public string PaymentTransactionId { get; set; }
    public decimal RefundAmount { get; set; }
}

using System;
public class RefundResponse
{
    public bool Success { get; set; }
    public string Message { get; set; }
}
·         The RefundRequest is sent to the RefundService as the single Refund method parameter, and RefundResponse is returned by the RefundService with the status of the refund transaction.
·         RefundRequest ‡K single Refund method parameter AvKv‡i RefundService G sent Kiv nq Ges RefundService refund  transaction status mn GKwU RefundResponse return K‡i|
·         Next classes Øq live WorldPay and PayPal web services represent K‡i|
·         Bnviv simple mock class hvnviv payment merchants offered functionality mg~n demonstrate K‡i|
·         MockWorldPayWebService Ges MockPayPalWebService bv‡gi `ywU new classes add Kwi
public class MockPayPalWebService
{
    public string ObtainToken(string accountName, string password)
    {
        return "xxxxxxxx-xxxxxxxxxxxxxx-xxxxxxxxx";
    }
    public string MakeRefund(decimal amount, string transactionId, string token)
    {
        return "Auth:0999";
    }
}
public class MockWorldPayWebService
{
    public string MakeRefund(decimal amount, string transactionId,
    string username, string password,
    string productId)
    {
        return "A_Success-09901";
    }
}
·         Payment merchants Gi mv‡_ RefundService interact Kivi Rb¨ PaymentServiceBase class add Ki‡Z n‡e|
public abstract class PaymentServiceBase
{
    public abstract string Refund(decimal amount, string transactionId);
}

·         WorldPayPayment I PayPalPayment class Øq PaymentServiceBase class ‡_‡K inherit n‡e Ges Adapter pattern use K‡i real web service API mg~n wrap Ki‡e|
·         RefundService  PaymentServiceBase class Gi mv‡_ interact Ki‡Z cvi‡e Ges †Kvb real implementation Gi mv‡_ Bnv dealing Ki‡e †m m¤ú‡K© completely ignore _vK‡e KviY Df‡qi behave same
·         GLb Avgiv `ywU merchant adapter create Ki‡ev-


·         PayPalPayment bv‡g GKwU class create Ki‡ev Ges Dnv‡K PaymentServiceBase class Øviv inherit Ki‡ev|

public class PayPalPayment : PaymentServiceBase
{
    public string AccountName { get; set; }
    public string Password { get; set; }
    public override string Refund(decimal amount, string transactionId)
    {
        MockPayPalWebService payPalWebService = new MockPayPalWebService();
        string token = payPalWebService.ObtainToken(AccountName, Password);
        string response = payPalWebService.MakeRefund(amount, transactionId, token);
        return response;
    }
}
·         ‡h‡nZz PayPal web service Gi †h‡Kvb transaction Gi mv‡_ GKwU  token Gi `iKvi ZvB token obtain Kivi Rb¨ cÖ_‡g Aek¨B log in n‡Z n‡e| GRb¨ AccountName and Password bv‡g `ywU extra properties include Ki‡Z n‡e
·         The Refund method creates a new instance of the web service (a mock object in this example), obtains a transaction token by logging in, and then performs the refund before returning the result to the calling code.
·         Refund method web service Gi new instance create Ki‡e, logging in Gi gva¨‡g transaction token obtain Ki‡e Ges calling code Gi result returning Gi c~‡e© refund perform Ki‡e|
·         Next, you can add the implementation for the WorldPay merchant adapter.
·          Next Avgiv WorldPay merchant adapter implementation add Ki‡ev hvi code listing wb¤œiƒc-

public class WorldPayPayment : PaymentServiceBase
{
    public string AccountId { get; set; }
    public string AccountPassword { get; set; }
    public string ProductId { get; set; }
    public override string Refund(decimal amount, string transactionId)
    {
        MockWorldPayWebService worldPayWebService = new MockWorldPayWebService();
        string response = worldPayWebService.MakeRefund(
        amount, transactionId, AccountId, AccountPassword, ProductId);
        return response;
    }
}
·         Again logging Gi Rb¨ `ywU extra properties add Ki‡ev Ges Z…ZxqwU †h  product Avgiv refund Ki‡ev Zvnv specify K‡i|
·         Actual Refund method implementation PayPalPayment class Gi Refund method ‡_‡K simpler KviY G‡ÿ‡Î refund Gi Rb¨ †Kvb token obtain Ki‡Z nq bv|

·         GLb Avgiv PaymentServiceFactory class create Ki‡ev | GB class wU payment adapter Gi concrete implementation Gi Rb¨ responsible
public class PaymentServiceFactory
{
    public static PaymentServiceBase GetPaymentServiceFrom(PaymentType paymentType)
    {
        switch (paymentType)
        {
            case PaymentType.PayPal:
                return new PayPalPayment();
            case PaymentType.WorldPay:
                return new WorldPayPayment();
            default:
                throw new ApplicationException("No Payment Service available for " + paymentType.ToString());
        }
    }
}

·         Dc‡iv³ GetPaymentServiceFrom method PaymentType enum ‡K argument wnmv‡e Pass K‡i | D³ argument matching K‡i concrete payment adapter implementation K‡i|
·         GLb Avgiv RefundService class add Ki‡ev|
public class RefundService
    {
        public RefundResponse Refund(RefundRequest refundRequest)
        {
            PaymentServiceBase paymentService = PaymentServiceFactory
            .GetPaymentServiceFrom(refundRequest.Payment);
            RefundResponse refundResponse = new RefundResponse();
            if ((paymentService as PayPalPayment) != null)
            {
                ((PayPalPayment)paymentService).AccountName = "Scott123-PP";
                ((PayPalPayment)paymentService).Password = "ABCXYZ-PP";
            }
            if ((paymentService as WorldPayPayment) != null)
            {
                ((WorldPayPayment)paymentService)
                .AccountId = "Scott123-WP";
                ((WorldPayPayment)paymentService)
                .AccountPassword = "ABCXYZ-WP";
                ((WorldPayPayment)paymentService).ProductId = "1";
            }
            string merchantResponse =
            paymentService.Refund(refundRequest.RefundAmount, refundRequest.PaymentTransactionId);
            refundResponse.Message = merchantResponse;
            if (merchantResponse.Contains("A_Success") ||
            merchantResponse.Contains("Auth"))
                refundResponse.Success = true;
            else
                refundResponse.Success = false;
            return refundResponse;
        }
    }
·         It should be immediately obvious that there is a problem.
·         Dc‡iv³ code G GKwU problem Av‡Q|
·         Best efforts Gi Rb¨ subtype †K base type Gi substitute wnmv‡e use Kiv hv‡e bv KviY adapter Gi cÖwZwU implementation differently handled n‡e|
·         wb‡¤œv³ code snippet fv‡jvfv‡e Observe Ki‡j †`Lv hv‡e base class Gi downcasting another code smell contain K‡i hvnv LSP rule break K‡i|
if ((paymentService as PayPalPayment) != null)
        {
            ((PayPalPayment)paymentService).AccountName = "Scott123-PP";
            ((PayPalPayment)paymentService).Password = "ABCXYZ-PP";
        }
        if ((paymentService as WorldPayPayment) != null)
        {
            ((WorldPayPayment)paymentService)
            .AccountId = "Scott123-WP";
            ((WorldPayPayment)paymentService)
            .AccountPassword = "ABCXYZ-WP";
            ((WorldPayPayment)paymentService).ProductId = "1";
        }
·         Return code section I LSP rule break K‡i KviY Avgv‡`i hv `iKvi Zv n‡jv mKj subtype Gi mKj case mg~n handle Kiv| wKš‘ Avgiv subtype specific code ensure bv K‡i subtype †K substitute Ki‡Z cvwi bv|

if (merchantResponse.Contains("A_Success") ||
        merchantResponse.Contains("Auth"))
            refundResponse.Success = true;
        else
            refundResponse.Success = false;
·         GB mKj issue mg~n Avgiv Lye mn‡RB resolve Ki‡Z cvwi|
·         cÖ_‡g Avgiv downcasting problem tackle Ki‡ev|
·         Bnv clear †h respective merchant Gi login credential e¨vZxZ web services methods call Kiv hv‡e bv|
·         ‡h‡nZz Dfq adapter login credential values Gi Dci depend myZivs G‡`i‡K constructor Gi parameter wnmv‡eI pass Kwi| d‡j Dnv‡`i e¨vZxZ adapter creation I m¤¢e n‡e bv|
·         myZivs Avgv‡`i WorldPayPayment I PayPalPayment class ؇qi code update Kwi wb¤œiƒ‡c-
public class WorldPayPayment : PaymentServiceBase
{
    public WorldPayPayment(string accountId, string accountPassword,
    string productId)
    {
        this.AccountId = accountId;
        this.AccountPassword = accountPassword;
        this.ProductId = productId;
    }
    public string AccountId { get; set; }
    public string AccountPassword { get; set; }
    public string ProductId { get; set; }
    public override string Refund(decimal amount, string transactionId)
    {
        MockWorldPayWebService worldPayWebService = new MockWorldPayWebService();
        string response = worldPayWebService.MakeRefund(
        amount, transactionId, AccountId, AccountPassword, ProductId);
        return response;
    }
}
public class PayPalPayment : PaymentServiceBase
{
    public PayPalPayment(string accountName, string password)
    {
        this.AccountName = accountName;
        this.Password = password;
    }
    public string AccountName { get; set; }
    public string Password { get; set; }
    public override string Refund(decimal amount, string transactionId)
    {
        MockPayPalWebService payPalWebService = new MockPayPalWebService();
        string token = payPalWebService.ObtainToken(AccountName, Password);
        string response = payPalWebService.MakeRefund(amount, transactionId, token);
        return response;
    }
}
·          ‡h‡nZz adapter class ؇qi constructor G argument pass Kiv n‡q‡Q ZvB PaymentServiceFactory class †K I wb¤œiƒ‡c update Ki‡Z n‡e-
public class PaymentServiceFactory
{
    public static PaymentServiceBase GetPaymentServiceFrom(PaymentType paymentType)
    {
        switch (paymentType)
        {
            case PaymentType.PayPal:
                return new PayPalPayment("Scott123-PP", "ABCXYZ-PP");
            case PaymentType.WorldPay:
                return new WorldPayPayment("Scott123-WP", "ABCXYZ-WP", "1");
            default:
                throw new ApplicationException(
                "No Payment Service available for " + paymentType.ToString());
        }
    }
}
·         GLb mn‡RB RefundService class return Kiv hv‡e  Ges downcasting issue remove Kiv hv‡e|

·         The second problem with the RefundService class as it stands is the refund transaction response.
·         RefundService class Gi second problem n‡”Q Bnv refund transaction response K‡i|
·         Currently, the RefundService class has to inspect the result of the transaction and ensure that it matches the authorization criteria of one of the subtypes, which again breaks the LSP.
·         GLb RefundService class †K result inspect Ki‡Z nq Ges ensure Ki‡Z nq †h Bnv †h †Kvb GKwU subtype Gi authorization criteria match K‡i|
·         Avgiv return type †K string †_‡K RefundResponse object G change K‡i Lye mn‡RB Bnvi mgvavb Ki‡Z cvi‡ev-
·         GRb¨ Avgiv PaymentServiceBase, PayPalPayment, WorldPayPayment class ‡K wb¤œiƒ‡c Update Ki‡ev-

public abstract class PaymentServiceBase
{
    public abstract RefundResponse Refund(decimal amount, string transactionId);
}


public class WorldPayPayment : PaymentServiceBase
{
    public WorldPayPayment(string accountId, string accountPassword,
    string productId)
    {
        this.AccountId = accountId;
        this.AccountPassword = accountPassword;
        this.ProductId = productId;
    }
    public string AccountId { get; set; }
    public string AccountPassword { get; set; }
    public string ProductId { get; set; }
    public override RefundResponse Refund(decimal amount, string transactionId)
    {
        RefundResponse refundResponse = new RefundResponse();
        MockWorldPayWebService worldPayWebService = new MockWorldPayWebService();
        string response = worldPayWebService.MakeRefund
        (amount, transactionId, AccountId, AccountPassword, ProductId);
        refundResponse.Message = response;
        if (response.Contains("A_Success"))
            refundResponse.Success = true;
        else
            refundResponse.Success = false;
        return refundResponse;
    }
}

public class PayPalPayment : PaymentServiceBase
{
    public PayPalPayment(string accountName, string password)
    {
        this.AccountName = accountName;
        this.Password = password;
    }
    public string AccountName { get; set; }
    public string Password { get; set; }
    public override RefundResponse Refund(decimal amount, string transactionId)
    {
        MockPayPalWebService payPalWebService = new MockPayPalWebService();
        RefundResponse refundResponse = new RefundResponse();
        string token = payPalWebService.ObtainToken(AccountName, Password);
        string response = payPalWebService.MakeRefund(amount, transactionId, token);
        refundResponse.Message = response;
        if (response.Contains("Auth"))
            refundResponse.Success = true;
        else
            refundResponse.Success = false;
        return refundResponse;
    }
}
·         GLb refund successful n‡j individual subtype Lye mn‡RB Zv determine Ki‡Z cvi‡e KviY
·         GLv‡b specific transaction response Gi mv‡_ simple boolean flag return K‡i|
·         The RefundService class can now be updated to treat the subtype in the same manner as the base type, with no need to downcast or check for a specific subtype behavior.
·         GLb RefundService class update Ki‡ev hvnv‡Z Dnv mKj subtype †K base type Gi b¨vq treat Ki‡Z cv‡i ( †Kvb downcast Gi cÖ‡qvRb co‡e bv wKsev GKwU specific subtype behavior check Gi cÖ‡qvRb co‡e bv)
public class RefundService
{
    public RefundResponse Refund(RefundRequest refundRequest)
    {
        PaymentServiceBase paymentService =
        PaymentServiceFactory.GetPaymentServiceFrom(refundRequest.Payment);
        RefundResponse refundResponse;
        refundResponse = paymentService.Refund
        (refundRequest.RefundAmount, refundRequest.PaymentTransactionId);
        return refundResponse;
    }
}
·         bx‡Pi Diagram Gi gva¨‡g refactoring Gi Rb¨ e¨eüZ mKj Class mg~n †`Lv‡bv n‡jv-
 



কোন মন্তব্য নেই:

একটি মন্তব্য পোস্ট করুন