Single Responsibility Principle Refactoring

This lists how to [[refactor]] the Single Responsibility Principle Demo code for better Single Responsibility Principle.

First we are creating an [[abstraction class]] for the payment model.

namespace SOLID.SRP.Refactored
{
    public abstract class PaymentModel
    {
        protected TicketDetails _ticketDetails;

        protected PaymentModel(TicketDetails ticketDetails)
        {
            _ticketDetails = ticketDetails;
        }

        public abstract void BuyTicket();
    }
}

Then we are going to implement an [[Interface]] for credit card payments.

namespace SOLID.SRP.Refactored
{
    public interface ICanPayViaCreditCard
    {
        void Charge(TicketDetails ticketDetails, PaymentDetails paymentDetails);
    }
}

Then we are going to implement bank gateway interface

namespace SOLID.SRP.Refactored
{
    public class BankGateway : ICanPayViaCreditCard
    {
        void Charge(TicketDetails ticketDetails, PaymentDetails paymentDetails)
        {
            // charge
        }
    }
}

Then we are implementing OnlinePayment class that derives the PaymentModel.

namespace SOLID.SRP.Refactored
{
    private readonly PaymentDetails _payment;
    private readonly ICanPayViaCreditCard _bankGateway;


    public class OnlinePayment : PaymentModel
    {
        public OnlinePayment(TicketDetails ticketDetails, PaymentDetails paymentDetails) : base(ticketDetails)
        {
           _payment = payment;
           _bankGateway = new BankGateway();
        }
    }

    public override void BuyTicket()
    {
        _bankGateway.ChargeCard(base._ticketDetails, _payment);
    }
}

Then we are adding an interface for paying with cash.

namespace SOLID.SRP.Refactored
{
    public interface ICanOperateWithCash
    {
        void AcceptCash();
        void DispenseChange();
    }
}

Then implement the inheritor for this interface:

namespace SOLID.SRP.Refactored
{
    public class PosTerminalPayment: PaymentModel, ICanOperateWithCash
    {
        private readonly Action _onPayChangeToMobilePhone;
        private decimal _cashAccepted;

        public PosTerminalPayment(TicketDetails ticketDetails, Action onPayChangeToMobilePhone) : base(ticketDetails)
        {
            _onPayChangeToMobilePhone = onPayChangeToMobilePhone;
        }

        public override void BuyTicket()
        {
            AcceptCash();
            DispenseChange();
        }

        public void AcceptCash()
        {
            Random r = new Random();
            _cashAccepted = r.Next((int) _ticketDetails.Price, (int) _ticketDetails.Price + 1000);
        }

        public void DispenseChange()
        {
            if(_cashAccepted > _ticketDetails.Price && !TryToDispense())
            {
                _onPayChangeToMobilePhone?.Invoke();
            }
        }

        private bool TryToDispense()
        {
            // issue a command for dispensing
            return false;
        }
    }
}