Or, see the corresponding FP version.
There should be only one reason to change a module.
For instance, you fixed a part in your module which manages A.
Yet, you found out it impacted another part of the module which manages B.
If such the case, then you are violating SRP (Single Responsibility Principle).
Split the jobs into different modules.
If you are in hurry, you can check out the shorter version.
This is a program for managing payment methods and address.
You will implement corresponding methods, and see if you designed the program
in a way which would not go against SRP (Single Responsibility Principle).
For the following class, implement:
- A method for making payments (e.g. "14.99 USD").
- A method for updating address (e.g. "Scarsdale, New York 10583")
enum PaymentMethod {
CreditCard = 0,
DebitCard = 1,
PayPal = 2,
Combini = 3,
CashOnDelivery = 4,
}
class Payment {}
In short, the bellow code has a problem for having 2 different jobs within 1 class.
enum PaymentMethod {
CreditCard = 0,
DebitCard = 1,
PayPal = 2,
Combini = 3,
CashOnDelivery = 4,
}
class Payment {
private address: string;
public update_address(address: string) {
this.address = address;
}
public pay(method: PaymentMethod, price: number) {
if (method === PaymentMethod.CreditCard) {}
if (method === PaymentMethod.DebitCard) {}
if (method === PaymentMethod.PayPal) {}
if (method === PaymentMethod.Combini) {}
if (method === PaymentMethod.CashOnDelivery) {}
}
}
{
const payment = new Payment();
// Updating the address
payment.update_address('Scarsdale, New York 10583');
// Make a payment
payment.pay(PaymentMethod.PayPal, 14.99);
}
SRP (Single Responsibility Principle) states
there should be only one reason to change a module.
If you were asked to add a new payment method,
it means nothing more than adding a new payment method.
But, with the above, it clearly has an impact on another part,
where the fix may cause the program to fail in updating user's address.
1_single_responsibility/oop/after_bad.ts
- Or, FP version:
1_single_responsibility/fp/after_bad.js
Instead, we should separate the class into 2 classes
so that each would contain only 1 action.
enum PaymentMethod {
CreditCard = 0,
DebitCard = 1,
PayPal = 2,
Combini = 3,
CashOnDelivery = 4,
}
// Here's the first class.
class Account {
private address: string;
public update_address(address: string) {
this.address = address;
}
}
// Here's the second class.
class Payment {
public pay(
account: Account,
method: PaymentMethod,
price: number
) {
if (method === PaymentMethod.CreditCard) {}
if (method === PaymentMethod.DebitCard) {}
if (method === PaymentMethod.PayPal) {}
if (method === PaymentMethod.Combini) {}
if (method === PaymentMethod.CashOnDelivery) {}
}
}
{
// Updating the address
const account = new Account();
account.update_address('Scarsdale, New York 10583');
// Make a payment
const payment = new Payment();
payment.pay(account, PaymentMethod.PayPal, 14.99);
}
1_single_responsibility/oop/after_good.ts
- Or, FP version:
1_single_responsibility/fp/after_good.js
To quickly grasp the idea behind, have a look at this shorter version:
When fixing pay
, it will affect updateAddress
.
class Payment {
private address: string
// Job #1
public update_address(address: string) {
this.address = address
}
// Job #2
public pay(method: PaymentMethod, price: number) {
if (method === 'CreditCard') {}
if (method === 'DebitCard') {}
if (method === 'PayPal') {}
if (method === 'Combini') {}
if (method === 'CashOnDelivery') {}
}
}
Split them into different contexts so that your fix will not affect others.
// Split #1
class Account {
private address: string
public update_address(address: string) {
this.address = address
}
}
// Split #2
class Payment {
public pay(
account: Account,
method: PaymentMethod,
price: number
) {
if (method === 'CreditCard') {}
if (method === 'DebitCard') {}
if (method === 'PayPal') {}
if (method === 'Combini') {}
if (method === 'CashOnDelivery') {}
}
}
Do you see how they differ?