How to declare variable that is for generic type of instance?
In controller, I need to create instance that depend on payment type, and each class has different type of parameter. That's why I used generic type. But I don't know what type I need to set to define variable for each payment class.
Models for parameter
public class PaymentModel
{
public string orderNo { get; set;}
}
public class CCPaymentModel : PaymentModel
{
public string CCNo {get; set;}
public string expDate {get; set;}
}
public class PaypalPaymentModel : PaymentModel
{
public string paypalID {get; set;}
}
public class GooglePaymentModel : PaymentModel
{
public string googleID {get; set;}
}
Interface class, I use Generic type parameter because each payment type need different type of parameter.
public interface IPayment<T> where T : PaymentModel
{
void makePayment(string orderNo);
void makeRefund(T refundInfo);
}
Models,
public class SagePayment
: IPayment<CreditCardPaymentInfo>
{
public void MakePayment( CreditCardPaymentInfo creditCardPaymentInfo ) {
// make payment
}
public void MakeRefund( CreditCardPaymentInfo creditCardPaymentInfo ) {
// make refund
}
}
public class GooglePayment
: IPayment<GooglePaymentModel>
{
public void MakePayment( GooglePaymentModel paymentInfo ) {
// make payment
}
public void MakeRefund( GooglePaymentModel paymentInfo ) {
// make refund
}
}
public class PaypalPayment
: IPayment<PayPalPaymentModel>
{
public void MakePayment( PayPalPaymentModel paymentInfo ) {
// make payment
}
public void MakeRefund( PayPalPaymentModel paymentInfo ) {
// make refund
}
}
Controller (Create instance)
public void Charge(string paytype,orderNo){
IPayment<???> paymentProcess; // //Error 1 Using the generic type 'com.WebUI.Models.IPayment<T>' requires 1 type arguments
Object payinfo; //
if (Regex.IsMatch(paytype, "^Credit Card"))
{
paymentProcess = new SagePayment();
payinfo = getPaymentInfo(paytype, orderNo); // it return CCPaymentModel type object
}
else if (Regex.IsMatch(paytype, "^PayPal"))
{
paymentProcess = new PayPalPayment();
payinfo = getPaymentInfo(paytype, orderNo); // it return PaypalPaymentModel type object
}
else if (Regex.IsMatch(paytype, "^Google"))
{
paymentProcess = new GooglePayment();
payinfo = getPaymentInfo(paytype, orderNo); // it return GooglePaymentModel type object
}
paymentProcess.MakePayment(payinfo);
}
To avoid error, I could this,
public void Charge(string paytype,orderNo){
if (Regex.IsMatch(paytype, "^Credit Card"))
{
IPayment<CCPaymentModel> paymentProcess = new SagePayment();
payinfo = getPaymentInfo(paytype, orderNo);
paymentProcess.MakePayment(payinfo);
}
else if (Regex.IsMatch(paytype, "^PayPal"))
{
IPayment<PaypalPaymentModel> paymentProcess = new PayPalPayment();
payinfo = getPaymentInfo(paytype, orderNo);
paymentProcess.MakePayment(payinfo);
}
else if (Regex.IsMatch(paytype, "^Google"))
{
IPayment<GooglePaymentModel> paymentProcess = new GooglePayment();
payinfo = getPaymentInfo(paytype, orderNo);
paymentProcess.MakePayment(payinfo);
}
}
public void Refund(string paytype,orderNo){
IPayment<???> paymentProcess; // //Error 1 Using the generic type 'com.WebUI.Models.IPayment<T>' requires 1 type arguments
Object payinfo; //
if (Regex.IsMatch(paytype, "^Credit Card"))
{
paymentProcess = new SagePayment();
payinfo = getPaymentInfo(paytype, orderNo); // it return CCPaymentModel type object
}
else if (Regex.IsMatch(paytype, "^PayPal"))
{
paymentProcess = new PayPalPayment();
payinfo = getPaymentInfo(paytype, orderNo); // it return PaypalPaymentModel type object
}
else if (Regex.IsMatch(paytype, "^Google"))
{
paymentProcess = new GooglePayment();
payinfo = getPaymentInfo(paytype, orderNo); // it return GooglePaymentModel type object
}
paymentProcess.MakeRefund(payinfo);
}
But I know it is not right way.
Anybody know, please advice me.
Given that your MakePayment
method does not require T
, it would probably be simplest to declare that in a separate interface. You could either make the generic interface extend the non-generic one:
// Capitalization fixed to comply with conventions
public interface IPayment
{
void MakePayment(string orderNo);
}
public interface IRefundPayment<T> : IPayment where T : PaymentModel
{
void MakeRefund(T refundInfo);
}
Or just have them as separate interface:
public interface IPaymentHandler
{
void MakePayment(string orderNo);
}
public interface IRefundHandler<T> where T : PaymentModel
{
void MakeRefund(T refundInfo);
}
Either way, you then only need the non-generic interface in your Charge
method.
You could make Charge()
and getPaymentInfo()
be generic with respect to the type of PaymentModel
:
void Charge<TPaymentModel>(...) where TPaymentModel : PaymentModel {
IPayment<TPaymentModel> payment = GetPayment<TPaymentModel>();
// ...
payment.MakePayment(getPaymentInfo<TPaymentModel>(...));
}
IPayment<TPaymentModel> GetPayment<TPaymentModel>() where TPaymentModel : IPaymentModel
{
// Create payment of appropriate type based on typeof(TPaymentModel)
}
TPaymentModel GetPaymentInfo(...) where TPaymentModel : PaymentModel
{
// Create payment model of appropriate type based on typeof(TPaymentModel)
}
This is still a little ugly, to resolve this you could introduce a few new classes to hide the correspondence between the types from the interfaces and make the design more sound:
/// Abstracts over different ways of making payments
interface IPaymentMaker
{
void MakePayment(string payType, long orderNo);
// MakeRefund etc.
}
/// Refactor code common to all payment types here, and handle the association
/// between payment and payment model.
class PaymentMakerBase<TPaymentModel> : IPaymentMaker
where TPaymentModel : IPaymentModel
{
void MakePayment(string payType, long orderNo)
{
NewPayment().MakePayment(NewPaymentModel(payType, orderNo));
}
abstract IPayment<TPaymentModel> NewPayment();
abstract TPaymentModel NewPaymentModel(string payType, long orderNo);
}
/// Handle only the differences between payment types that can't be put inside their
/// implementations
class PaypalPaymentMaker : PaymentMakerBase<PaypalPayment>
{
IPayment<PaypalPayment> NewPayment() { ... }
PaypalPayment NewPaymentModel(...) { ... }
}
static class PaymentMakerFactory
{
/// The only "not type safe" part, handles parsing the payType string and
/// resolving it to the correct `PaymentMaker`
public IPaymentMaker GetPaymentMaker(string payType)
{
if (Regex.IsMatch(payType, ...))
{
// return appropriate payment maker for the payType
}
else if (...)
{
// ...
}
}
}
Then, your controller code only looks like this:
PaymentMakerFactory.GetPaymentMaker(payType).MakePayment(payType, orderNo);
Obviously the above design could be improved by removing redundancies (payType probably isn't needed everywhere I include it), making it more "objecty" (instead of passing identical argument lists around), or more convenient ( PaymentMakerFactory
could probably be changed into a facade that creates the right payment maker and then calls MakePayment
right away).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.