mardi 18 mai 2021

Known ways of mixing a parent class and a child class code in the one method

Code sample:

public class BaseCustomer
{
    public long GetBalance()
    {
        var result = default(long);

        // 1. some BigBatch1 of code that identical for Wallet and Merchant

        // 2.A. Wallet specified code if GetBalance called for Wallet

        // 2.B. Merchant specified code if GetBalance called for Merchant

        // 3. some BigBatch2 of code that identical for Wallet and Merchant

        return result;
    }
}

public class Merchant : BaseCustomer { }

public class Wallett : BaseCustomer { }

....

void Main(){
    var wallet = new Wallet();
    var merchant = new Merchant();
            
    Console.WriteLine($"Wallet balance: {wallet.GetBalance()}");
    Console.WriteLine($"Merchant balance: {merchant.GetBalance()}");
}

Wallet and Merchant have GetBalance method. The method contains a lot of identical for both classes code (see commetns 1. and 3. from above code). Also, some parts of GetBalance method differ for Wallet (commetn 2.A.) and Merchant (comment 2.B.). Also, there may be much more mixes of identical and different code.

Task: GetBalance should work correctly for Wallet and Merchant.

I'm trying to find all known ways to solve the task. For now, I know the following ways:

1) Mark BaseCustomer.GetBalance as abstract and implement it in Wallet and Merchant. Pros: it will work, Cons: BigBatch1 and BigBatch2 will be doubled in Wallet and Merchant:

public abstract class BaseCustomer
{
    public abstract long GetBalance();
}

public class Wallet : BaseCustomer
{
    public override long GetBalance()
    {
        var result = default(long);

        // 1. some BigBatch1 of code that identical for Wallet and Merchant

        // 2.A. Wallet specified code

        // 3. some BigBatch2 of code that identical for Wallet and Merchant

        return result;
    }
}

public class Merchant : BaseCustomer
{
    public override long GetBalance()
    {
        var result = default(long);

        // 1. some BigBatch1 of code that identical for Wallet and Merchant

        // 2.B. Merchant specified code

        // 3. some BigBatch2 of code that identical for Wallet and Merchant

        return result;
    }
}

2) Option 1 + implementation of BigBatch1 and BigBatch2 as methods in BaseCustomer. Pros: It will work, no code will be doubled, Cons: methods for BigBatch1 and BigBatch2 don't make any sense on their own, their signatures may be very bulky, there may be BigBatch3/4/5 etc code fragments.

public abstract class BaseCustomer
{
    public abstract long GetBalance();

    protected BigBatch1OutParams DoBigBatch1(BigBatch1InParams @params) { /* some BigBatch1 of code that identical for Wallet and Merchant */ }

    protected BigBatch2OutParams DoBigBatch2(BigBatch2InParams @params) { /* some BigBatch2 of code that identical for Wallet and Merchant */ }
}

public class Wallet : BaseCustomer
{
    public override long GetBalance()
    {
        var result = default(long);

        var bb1 = DoBigBatch1(new BigBatch1InParams { ... });

        // 2.A. Wallet specified code if GetBalance called for Wallet

        var bb2 = DoBigBatch2(new BigBatch2InParams { ... });
        
        // some code

        return result;
    }
}

public class Merchant : BaseCustomer 
{
    public override long GetBalance()
    {
        var result = default(long);

        var bb1 = DoBigBatch1(new BigBatch1InParams { ... });

        // 2.B. Merchant specified code if GetBalance called for Merchant

        var bb2 = DoBigBatch2(new BigBatch2InParams { ... });

        // some code

        return result;
    }
}

3) Place 2.A. Wallet specified code and 2.B. Merchant specified code in abstact method of BaseCustomer. Pros: it will work, Cons: the same as in the Point 2, there may be DoSpecifiedWork1/2/3 etc code fragments.

public abstract class BaseCustomer
{
    public long GetBalance()
    {
        var result = default(long);

        // 1. some BigBatch1 of code that identical for Wallet and Merchant

        var t = DoSpecifiedWork(new ClassSpecifiedInParams { ... });

        // 3. some BigBatch2 of code that identical for Wallet and Merchant

        return result;
    }

    protected abstract ClassSpecifiedOutParams DoSpecifiedWork(ClassSpecifiedInParams @params);
}

public class Wallet : BaseCustomer
{
    protected override ClassSpecifiedOutParams DoSpecifiedWork(ClassSpecifiedInParams @params)
    {
        // 2.A. Wallet specified code
    }
}

public class Merchant : BaseCustomer
{
    protected override ClassSpecifiedOutParams DoSpecifiedWork(ClassSpecifiedInParams @params)
    {
        // 2.B. Merchant specified code
    }
}

What other options do you know? Maybe, the task may be implemented more elegantly by additional abstraction level or by using some pattern? I would be grateful for some code examples.

Aucun commentaire:

Enregistrer un commentaire