Введение в принципы SOLID

solid best practic

SOLID — это набор принципов объектно-ориентированного проектирования, разработанных для создания гибких, поддерживаемых и расширяемых программных систем. Эти принципы помогают разработчикам создавать код, который легче понимать, тестировать и поддерживать.

История возникновения SOLID

Принципы SOLID сформировались в рамках развития объектно-ориентированного программирования (ООП). В 1990-е годы программное обеспечение становилось все более сложным, и разработчики сталкивались с проблемами, связанными с поддержкой и развитием крупных проектов. В это время Роберт Мартин и другие лидеры индустрии начали разрабатывать и популяризировать подходы, которые помогали бы справляться с этими проблемами.

Сам термин “SOLID” впервые появился в начале 2000-х, когда Майкл Фезерс (Michael Feathers) использовал его для обозначения пяти основных принципов, описанных Робертом Мартином. Название “SOLID” является акронимом, образованным от первых букв этих принципов:

  1. Single Responsibility Principle (Принцип единственной ответственности)
  2. Open/Closed Principle (Принцип открытости/закрытости)
  3. Liskov Substitution Principle (Принцип подстановки Лисков)
  4. Interface Segregation Principle (Принцип разделения интерфейсов)
  5. Dependency Inversion Principle (Принцип инверсии зависимостей)

Причины популярности SOLID

  1. Повышение качества кода: Применение принципов SOLID позволяет создавать код, который легче поддерживать и расширять. Это особенно важно в условиях крупных и долгосрочных проектов, где изменение одного компонента не должно ломать всю систему.
  2. Улучшение тестируемости: Принципы SOLID способствуют созданию модульного кода, который легче тестировать. Это критично для разработки программного обеспечения, где важны стабильность и отсутствие регрессий.
  3. Гибкость и расширяемость: SOLID обеспечивает создание систем, которые легко адаптируются к изменениям требований. Это снижает затраты на доработку и модернизацию программного обеспечения, что особенно ценно в динамично меняющихся бизнес-условиях.
  4. Простота понимания и работы в команде: Код, написанный с учетом принципов SOLID, легче читать и понимать. Это упрощает работу в команде, где множество разработчиков могут работать над одним проектом.
  5. Решение типичных проблем объектно-ориентированного программирования: Принципы SOLID были разработаны для борьбы с общими проблемами ООП, такими как жесткие зависимости между компонентами и нарушение инкапсуляции. Они помогают создать архитектуру, которая поддерживает ключевые концепции ООП.

Применение в индустрии

Со временем SOLID стал стандартом де-факто для многих компаний и разработчиков. Его применение стало обязательным на многих проектах, особенно в тех случаях, когда требуется высокая надежность и поддерживаемость программного обеспечения. Принципы SOLID интегрировались в методологии разработки, такие как Agile и DevOps, и стали основой для обучения молодых разработчиков.

Разберем каждый из них поподробнее

Принцип единственной ответственности (Single Responsibility Principle, SRP)

Принцип единственной ответственности гласит, что каждый класс или модуль должен отвечать только за одну задачу. Это упрощает код и уменьшает количество ошибок, возникающих из-за изменения логики.

Пример на PHP:


class Order {
    public function calculateTotal() {
        // Логика расчета общей стоимости заказа
    }
}

class OrderRepository {
    public function save(Order $order) {
        // Логика сохранения заказа в базу данных
    }
}

Пример на Go:

type Order struct {
    // Поля заказа
}

func (o Order) CalculateTotal() float64 {
    // Логика расчета общей стоимости заказа
    return 0.0
}

type OrderRepository struct {}

func (r OrderRepository) Save(order Order) {
    // Логика сохранения заказа в базу данных
}

Пример на C#:

public class Order
{
    public decimal CalculateTotal()
    {
        // Логика расчета общей стоимости заказа
    return 0m;
    }
}

public class OrderRepository
{
    public void Save(Order order)
    {
        // Логика сохранения заказа в базу данных
    }
}

Принцип открытости/закрытости (Open/Closed Principle, OCP)

Принцип открытости/закрытости утверждает, что программные сущности должны быть открыты для расширения, но закрыты для изменения. Это означает, что поведение системы можно расширить, не изменяя существующий код.

Пример на PHP:

abstract class PaymentMethod {
    abstract public function pay($amount);
}

class CreditCardPayment extends PaymentMethod {
    public function pay($amount) {
        // Логика оплаты кредитной картой
    }
}

class PayPalPayment extends PaymentMethod {
    public function pay($amount) {
        // Логика оплаты через PayPal
    }
}

Пример на Go:

type PaymentMethod interface {
    Pay(amount float64)
}

type CreditCardPayment struct{}

func (p CreditCardPayment) Pay(amount float64) {
     // Логика оплаты кредитной картой
}

type PayPalPayment struct{}

func (p PayPalPayment) Pay(amount float64) {
    // Логика оплаты через PayPal
}

Пример на C#:

public abstract class PaymentMethod
{
    public abstract void Pay(decimal amount);
}

public class CreditCardPayment : PaymentMethod
{
    public override void Pay(decimal amount)
    {
        // Логика оплаты кредитной картой
    }
}

public class PayPalPayment : PaymentMethod
{
    public override void Pay(decimal amount)
    {
        // Логика оплаты через PayPal
    }
}

Принцип подстановки Лисков (Liskov Substitution Principle, LSP)

Принцип подстановки Лисков требует, чтобы объекты подклассов могли заменять объекты суперклассов без изменения поведения программы. Это обеспечивает корректное использование полиморфизма.

Пример на PHP:

class Rectangle {
    protected $width;
    protected $height;

    public function setWidth($width) {
        $this->width = $width;
    }

    public function setHeight($height) {
        $this->height = $height;
    }

    public function getArea() {
        return $this->width * $this->height;
    }
}

class Square extends Rectangle {
    public function setWidth($width) {
        $this->width = $this->height = $width;
    }

    public function setHeight($height) {
        $this->width = $this->height = $height;
    }
}

Пример на Go:

type Shape interface {
    Area() float64
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

type Square struct {
    Side float64
}

func (s Square) Area() float64 {
    return s.Side * s.Side
}

Пример на C#:

public class Rectangle
{
    public virtual double Width { get; set; }
    public virtual double Height { get; set; }

    public virtual double GetArea()
    {
          return Width * Height;
    }
}

public class Square : Rectangle
{
    public override double Width
    {
        set { base.Width = base.Height = value; }
    }

     public override double Height
     {
        set { base.Width = base.Height = value; }
    }
}

Принцип разделения интерфейса (Interface Segregation Principle, ISP)

Принцип разделения интерфейса гласит, что клиенты не должны зависеть от интерфейсов, которые они не используют. Это помогает избежать чрезмерной связанности и упрощает поддержку кода.

Пример на PHP:

interface Workable {
    public function work();
}

interface Eatable {
    public function eat();
}

class HumanWorker implements Workable, Eatable {
    public function work() {
        // Логика работы
    }

    public function eat() {
        // Логика обеда
    }
}

class RobotWorker implements Workable {
    public function work() {
        // Логика работы робота
    }
}

Пример на Go:

type Workable interface {
    Work()
}

type Eatable interface {
    Eat()
}

type HumanWorker struct{}

func (h HumanWorker) Work() {
    // Логика работы
}

func (h HumanWorker) Eat() {
    // Логика обеда
}

type RobotWorker struct{}

func (r RobotWorker) Work() {
    // Логика работы робота
}

Пример на C#:

public interface IWorkable
{
     void Work();
}

public interface IEatable
{
    void Eat();
}

public class HumanWorker : IWorkable, IEatable
{
    public void Work()
    {
        // Логика работы
    }

    public void Eat()
    {
        // Логика обеда
    }
}

public class RobotWorker : IWorkable
{
    public void Work()
    {
        // Логика работы робота
    }
}

Принцип инверсии зависимостей (Dependency Inversion Principle, DIP)

Принцип инверсии зависимостей говорит о том, что высокоуровневые модули не должны зависеть от низкоуровневых модулей. Оба типа модулей должны зависеть от абстракций.

Пример на PHP:

interface DatabaseConnectionInterface {
    public function connect();
}

class MySQLConnection implements DatabaseConnectionInterface {
    public function connect() {
        // Логика подключения к MySQL
    }
}

class PasswordReminder {
    private $dbConnection;

    public function __construct(DatabaseConnectionInterface $dbConnection) {
        $this->dbConnection = $dbConnection;
    }
}

Пример на Go:

type DatabaseConnection interface {
    Connect()
}

type MySQLConnection struct{}

func (c MySQLConnection) Connect() {
    // Логика подключения к MySQL
}

type PasswordReminder struct {
    DBConnection DatabaseConnection
}

func NewPasswordReminder(dbConnection DatabaseConnection) PasswordReminder {
    return PasswordReminder{DBConnection: dbConnection}
}

Пример на C#:

public interface IDatabaseConnection
{
    void Connect();
}

public class MySQLConnection : IDatabaseConnection
{
    public void Connect()
    {
        // Логика подключения к MySQL
    }
}

public class PasswordReminder
{
    private readonly IDatabaseConnection _dbConnection;

    public PasswordReminder(IDatabaseConnection dbConnection)
    {
        _dbConnection = dbConnection;
    }
}

Заключение

Применение принципов SOLID помогает создавать устойчивую и масштабируемую архитектуру как в монолитных, так и в микросервисных системах. Приведенные примеры на языках PHP, Go и C# демонстрируют, как можно реализовать эти принципы на практике, что позволит разработчикам строить приложения, которые проще поддерживать, тестировать и расширять.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *