SOLID Principals Explained in JavaScript
SOLID OOD Principals in JavaScript
السلام عليكم ازيكم يا اصدقائي في البلوج دي هنشرح ان شاء الله مباديء ال SOLID باستفاضه باستخدام ال JavaScript، طبعاً انت تقدر تستخدمها بأي لغة تانيه، احنا بنستخدم المباديء دي عشان تنظملنا الكود اكتر وتخليه Scalable و Maintainable
1- Single Responsibility (SRP)
Every Class should have one responsibility.
سبب واحد انك تغير الكلاس
يعني مينفعش تخلي الكلاس بتاعك يقوم بكل المهام في البرنامج او حتى مهام ملهاش علاقة ببعض، لازم لكل كلاس مهمه محدده ومعروفه، ومعروف انت رايح تعدل فين بالظبط اللي ليه علاقة بالوظيفة
//before applying SRP
class User{
constructor(name){
this.name = name;
}
toJSON(){
return JSON.stringify(this);
}
}
//after applying SRP
class User{
constructor(name){
this.name = name;
}
}
class UserSerializer{
static toJSON(user){
return JSON.stringify(user);
}
}
في المثال البسيط ده فصلنا اللوجيك بتاع انشاء يوزر عن اللوجيك بتاع عرض معلوماته
لو خدنا مثال مثال عملي اكتر:
//before applying SRP
class User {
constructor(username,password){
this.username = username;
this.password = password;
}
authenticate(passwordInput){
return this.password === passwordInput;
}
save(){
db.saveUser(this);
}
}
const user = new User('Osama', '123');
if(user.authenticate('123')){
user.save()
}
//after applying SRP
class User {
constructor(username,password){
this.username = username;
this.password = password;
}
authenticate(passwordInput){
return this.password === passwordInput;
}
}
class UserDataManager {
static save(user){
const db = getDatabaseConnection();
db.saveUser(user);
}
}
const user = new User('Osama', '123');
if(user.authenticate('123')){
UserDataManager.save(user);
}
وبكدا نكون فصلنا ال UserDataManager عن الكلاس الرئيسي وبكدا تبقى ليه مهمه واحده
2- Open-closed Principle (OCP)
Codebase Should be open for extension and close for modifications.
أزود اه إنما معدلش في الكود
يعني الكود بتاعك يكون Flexible في طريقة كتابته بحيث لما تحب تعمل function تبقى ثابته في الكور من غير اما تتعدل مثلاً ب if conditions او switch statements ولكن تتعدل عن طريق الobjects اللي هتتبعتلها اللي على اساسها هيكون الoutput.
//before applying OCP
class AreaCalculator {
static calaculate(shape){
if(shape.type === 'cicle'){
return Math.PI * shape.radius ** 2;
} else if (shape.type === 'square') {
return shape.side * shape.side;
}
}
}
const circle = { type: 'cricle', radius: 5 };
const squrare = { type: 'square', radius: 4};
$ AreaCalculator.calculate(circle);
$ AreaCalculator.calculate(square);
//after applying OCP
class Circle {
constructor(radius) {
this.radius = radius;
}
area() {
return Math.PI * shape.radius ** 2;
}
}
class Square {
constructor(side) {
this.side = side;
}
area() {
return shape.side * shape.side;
}
}
class AreaCalculator {
static calculate(shape) {
return shape.area();
}
}
$ const circle1 = new Circle(5);
$ const square1 = new Square(4);
$ AreaCalculator.calculate(circle1);
$ AreaCalculator.calculate(square1);
3- Liskov Substitution Principle (LSP)
Object from Child and Parent Classes should have similar functionalities.
اقدر استعمل اي واحد من الparent او الchild واتوقع ان السلوك يبقى واحد
بمعنى مينفعش ت override وظائف ال parent class بشكل عشوائي ويكون نتيجة functions مختلفه من ال parent او ال child.
//before applying LSP
class Bird {
fly(){
console.log('this bird can fly');
}
}
class Duck extends Bird {
fly(){
console.log('Duck flying');
}
}
class Penguin extends Bird {
fly(){
throw new Error('penguins cannot fly');
}
}
function makeBirdFly(bird) {
bird.fly();
}
const duck = new Duck();
const penguin = new Eagle();
makeBirdFly(duck); // works fine
makeBirdFly(Penguin); //Error
//after applying LSP
class Bird {
}
class FlyingBird extends bird {
fly(){
console.log('this bird can fly')
}
}
class Duck extends Bird {
fly(){
console.log('Duck flying');
}
}
class Penguin extends Bird {
fly(){
throw new Error('penguins cannot fly');
}
}
function makeBirdFly(bird) {
if(bird instanceof FlyingBird){
bird.fly();
}else{
console.log('this bird cannot fly');
}
}
const duck = new Duck();
const penguin = new Eagle();
makeBirdFly(duck); // works fine
makeBirdFly(Penguin); //penguins cannot fly
4- Interface Segregation Principle (ISP)
Interfaces should be meaningful to one functionality.
قسم ال interface الكبيرة ل interfaces صغيرة محدده
بمعنى ان ال interfaces مينفعش تبقى لاكتر من حاجه احتمال كبير مستخدمهاش ولكن المفروض تبقى blue print لحاجات محدده احتمال كبير استخدمها في وظيفة محدده.
//before applying ISP
class Workable {
work() {
console.log('working on work');
}
eat() {
console.log('eating some food');
}
sleep() {
console.log('taking a nap');
}
}
function manageWork(worker){
worker.work();
}
//after applying ISP
class Workable {
work() {
console.log('working on work');
}
}
class Eatable {
eat() {
console.log('eating some food');
}
}
class Sleepable {
sleep() {
console.log('taking a nap');
}
}
function manageWork(workable){
workable.work();
}
5- Dependency Inversion Principle (DIP)
Higher level modules can’t depend on lower level modules
مينفعش ال higher level modules تعتمد على lower level modules ولكن لازم أحط abstraction layer في النص واخلي الاتنين يعتمدوا عليهم وبالتالي يحصل عكس في ال dependency.
//before applying DIP
class UsernamePasswordAuth {
authenticate(username, password) {
}
}
class User {
login(username, password){
const auth = new UsernamePasswordAuth();
return auth.authenticate(username, password);
}
}
// after applying DIP
class AbstractAuthMethod {
authenticate(credentials){
throw new Error('This error should be overridden');
}
}
class UsernamePasswordAuth extends AbstractAuthMethod {
authenticate({ username, password}) {
}
}
class EmailAuth extends AbstractAuthMethod {
authenticate({ email, token}) {
}
}
class User {
constructor(authMethod){
if(!authMethod instanceof AbstractAuthMethod){
throw new Error('invalid authentication mehtod');
}
this.authMethod = authMethod;
}
login(credentials) {
return this.authMethod.authenticate(credentials);
}
}
لو عجبك البلوج متنساش لايك وشير ❤️