·
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-
কোন মন্তব্য নেই:
একটি মন্তব্য পোস্ট করুন