SOLID Design Principles in Ruby
Tags: solid, ruby, oop
Published: 2024-07-02 04:57:04

**SOLID - dasturni yanada tushunarli, o'zgartirishga va kattalashtirishga imkon beruvchi principlar yig'indisi.**

- Single Responsibility Principle (SRP)
- Open/Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)

**1. Single Responsibility Principle**
Har bir class faqatgina bitta funksionalga javob berishi kerak. Ko'p funksional'larni bitta class'da yozilgan bo'lsa, o'sha classni o'zgartirish hamma funksionallarni o'zgartirishga olib kelishi mumkin.

```
class Employee
  def process_data
  end

  def send_message(message)
  end
end
```

masalan, bizda Employee class bor, unda process_data va send_message method'lari mavjud. send_message method'ga o'zgartirish kerak bo'lsa, Employee classni ichida o'zgartirishga to'g'ri keladi. Bundan tashqari, mijozlarga ham message yuborish kerak bo'lsa, Client class'ni ichida ham send_message methodini yozishimizga to'g'ri keladi.

```
class Employee
  def process_data; end
end

class Client
  def process_data; end
end

class Message
  def send_message(user, message)
  end
end

```
Agar yuboriladigan message o'zgartirilishi kerak bo'lsa, Message classni ichida o'zgartirishimiz kifoya.

**2. Open/Closed Principle (OCP)**
Classlar kengaytirish uchun ochiq, lekin o'zgartirish yoki refaktor qilish uchun yopiq bo'lishi kerak. Bu printsip classni imkoniyatini oshirish uchun allaqachon ishlab turgan classni o'zgartirmaslikni eslatadi. Masalan bizda InvoiceReport class bor, u hozircha orderni pdf va csv formatlarda generate qiladi. 

```
class InvoiceReport
  def initialize(order, type)
    @order = order
    @type = type
  end

  def generate
    case @type
    when 'pdf'
      # pdf generation
    when 'csv'
      # csv generation
    end
  end
end
```
Endi xls formatda ham report generate qilmoqchimiz, va InvoiceReport classga qo'shishimiz kerak. Buni oldini olish uchun Open/Closed Principle ishlatishimiz mumkin:

```
class InvoiceReport
  def initialize(order, klass)
    @order = order
    @klass = klass
  end

  def generate
    @klass.new(@order).generate
  end
end

class PdfGenerator
  def initialize(order)
    @order = order
  end

  def generate
    # Generate PDF report
    puts "PDF Report generated"
  end
end

class CsvGenerator
  def initialize(order)
    @order = order
  end

  def generate
    # Generate CSV report
    puts "CSV Report generated"
  end
end
```

**3. Liskov Substitution Principle**
**if S could be a sub-type of T, then objects of type T is also replaced with objects of type S** - Agar S class T classni child classi bo'lsa, T classni obyektlari S classni obyektlari bilan muvofiq bo'lishi kerak. 
```
class User
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def report
    raise 'Not implemented for User class'
  end
end

class Employee < User
  def initialize(name)
    super
  end

  def report
    puts "Report method called!!!"
  end
end
```
Agar child class o’zining ota class’i bilan bir xil amallarni bajara olmasa, bu xatolarga olib kelishi mumkin.

Agar sizda class mavjud bo’lsa va undan yangi yangi class yaratsangiz, u parent (ota) va yangi class esa child (farzand) class bo’ladi. Child class ota class bajara olgan barcha narsani qila olishi kerak. Bu jarayon Inheritance deyiladi.

Child class bir xil so’rovlarni qayta ishlashi va parent class bilan bir xil yoki bir turdagi natijani berishi kerak.

Ushbu tamoyil asosiy sinf yoki uning pastki sinfi hech qanday xatosiz bir xil tarzda ishlatilishi uchun o’zgarmaslikni ta’minlashga qaratilgan.

**4. Interface Segregation**
Interface Segregation tamoyili client ishlatmaydigan methodga bog'liq bo'lmasligini aytadi. Boshqacha qilib aytganda, bitta umumiy katta interfeysdan ko'ra classni o'zigagina tegishli interfeyslar yaratish g'oyasini qo'llaydi. 
```
module PrinterFunctions
  def print

  end

  def scan

  end

  def fax

  end
end

class Printer
  include PrinterFunctions
end
```

hamma printerlar scan yoki fax funksiyasini qo'llab quvvatlay olmaydi, va bu misol ISP ni buzadi.
```
module PrintFunctions
  def print

  end
end

module ScanFunctions
  def scan

  end
end

module FaxFunctions
  def fax

  end
end

class Samsung
  include PrintFunctions
  include ScanFunctions
end
```
Endi client o'ziga kerakli methodlarni ishlata oladi.

**5. Dependency Inversion Principle**
High-level class'lar low level class'larga qaram bo'lmasligi kerak. Har ikkalasi ham abstraktsiyaga qaram bo'lishi kerak. Dependency Inversion Liskov Substitution and Open-Closed principlari jamlanmasi.

```
class NotificationService
  def notify_via_email(message)
    # EmailNotifier classga to'g'ridan to'g'ri bog'liq
    email_notifier = EmailNotifier.new
    email_notifier.send_email(message)
  end
end

class EmailNotifier
  def send_email(message)
    puts "Sending email with message: #{message}"
    # Logic to send an email
  end
end

notification_service = NotificationService.new
notification_service.notify_via_email('Hello via Email!')
```
bu yerda muammolar:
1. NotificationService EmailNotifier classga bog'liqligi
2. Yangi Notification type qo'shmoqchi bo'lsak, NotificationService classga o'zgartirish kiritishga to'g'ri keladi.
Bu muammolarni Dependency Inversion prinsipiga binoan hal qilsak:
```
# notifier.rb, Abstraksiya yaratish
module Notifier
  def send_message(message)
    raise NotImplementedError, 'You must implement the send_message method'
  end
end

class EmailNotifier
  include Notifier

  def send_message(message)
    puts "Sending email with message: #{message}"
    # Logic to send an email
  end
end

# notification_service.rb, Notifier abstraksiyasini ishlatish
class NotificationService
  def initialize(notifier)
    @notifier = notifier
  end

  def notify(message)
    @notifier.send_message(message)
  end
end

email_notifier = EmailNotifier.new
notification_service = NotificationService.new(email_notifier)
notification_service.notify('Hello via Email!')
```
Bu bilan yuqori darajadagi class undan pastroq darajadagi classga bog'liq bo'lmaydi.