Від плутанини до ясності: Принципи SOLID з прикладами для .NET

pic

Розроблено freepik

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

Ми вже охопили багато теорії і коду до цього моменту, тож тепер давайте покажу вам, як створити базову логіку і перевести її в код Java. Якщо ви тільки починаєте працювати з Java, не хвилюйтеся! У цій статті я проведу вас через основні концепції програмування з простими прикладами, що показують як логічні інструкції, так і їхні еквіваленти в коді Java. Кожен приклад буде пояснений коментарями, щоб навіть той, хто не має попереднього досвіду, міг зрозуміти, як все працює.

1. Додавання чисел

Логічні інструкції:

Start //Точка початку коду  
Print "Введіть два числа";  
IN1 = 23;  
IN2 = 43;  
Variable Add = SUM(23, 43); // Це обчислить суму 23 та 43  
OUT(Add); // Це виведе суму 23 та 43

Код Java:

// Main - Точка початку виконання коду  
public class Main {  
 public static void main(String[] args) {  
 // Вивести "Введіть два числа" на екран  
 System.out.println("Введіть два числа");  

 // Оголосити та присвоїти значення змінним  
 int IN1 = 23; // Зберігаємо перше число в IN1  
 int IN2 = 43; // Зберігаємо друге число в IN2  

 // Обчислити суму IN1 та IN2 і зберегти її в змінній Add  
 int Add = IN1 + IN2; // SUM(23, 43) -> Додавання двох чисел разом  

 // Вивести результат додавання на екран  
 System.out.println(Add); // OUT(Add) -> Вивести суму  
 }  
}

Пояснення:

  1. System.out.println("Введіть два числа"); виводить повідомлення, яке запитує користувача ввести два числа.
  2. Змінні IN1 та IN2 зберігають значення 23 та 43 відповідно.
  3. Сума IN1 та IN2 зберігається в змінній Add.
  4. Нарешті, результат додавання виводиться за допомогою System.out.println(Add);.

2. Використання If та Else в сценарії

Логічні інструкції:

Start //Точка початку коду  
Print "Введіть число";  
IN1 = 10;  
IF (IN1 > 5) {  
 Print "Число більше за 5";  
}  
ELSE {  
 Print "Число не більше за 5";  
}

Код Java:

// Main - Точка початку виконання програми  
public class Main {  
 public static void main(String[] args) {  
 // Вивести "Введіть число" для запиту введення користувача  
 System.out.println("Введіть число");  

 // Оголосити змінну IN1 та присвоїти їй значення 10  
 int IN1 = 10;  

 // Перевірити, чи є IN1 більше за 5  
 if (IN1 > 5) {  
 // Якщо умова істинна, вивести це повідомлення  
 System.out.println("Число більше за 5");  
 } else {  
 // Якщо умова хибна, вивести це повідомлення  
 System.out.println("Число не більше за 5");  
 }  
 }  
}

Пояснення:

  1. if (IN1 > 5) перевіряє, чи є число більшим за 5.
  2. Якщо це так, програма виведе "Число більше за 5".
  3. Якщо умова хибна (тобто число не більше за 5), виконується блок else, і виводиться "Число не більше за 5".

3. Використання циклу For

Логічні інструкції:

Start //Точка початку коду  
Print "Лічити від 1 до 5";  
FOR (IN1 = 1; IN1 <= 5; IN1 = IN1 + 1) {  
 Print IN1;  
}

Код Java:

// Main - Точка початку виконання програми  
public class Main {  
 public static void main(String[] args) {  
 // Вивести повідомлення на екран  
 System.out.println("Лічити від 1 до 5");  

 // Використовуємо цикл for для лічби від 1 до 5  
 for (int IN1 = 1; IN1 <= 5; IN1++) {  
 // Вивести значення IN1 кожного разу, коли цикл виконується  
 System.out.println(IN1);  
 }  
 }  
}

Пояснення:

  1. Цикл for виконується від IN1 = 1 до IN1 = 5.
  2. IN1++ збільшує значення IN1 на 1 після кожної ітерації циклу.
  3. Значення IN1 виводиться на екран під час кожної ітерації, виводячи числа від 1 до 5.

4. Використання циклу While

Логічні інструкції:

Start //Точка початку коду  
Print "Лічити від 5 до 1";  
IN1 = 5;  
WHILE (IN1 >= 1) {  
 Print IN1;  
 IN1 = IN1 - 1;  
}

Код Java:

// Main - Точка початку виконання програми  
public class Main {  
 public static void main(String[] args) {  
 // Вивести повідомлення на екран  
 System.out.println("Лічити від 5 до 1");  

 // Ініціалізуємо значення IN1  
 int IN1 = 5;  

 // Використовуємо цикл while для лічби від 5 до 1  
 while (IN1 >= 1) {  
 // Вивести значення IN1  
 System.out.println(IN1);  
 // Зменшити IN1 на 1 після кожної ітерації циклу  
 IN1--;  
 }  
 }  
}

Пояснення:

  1. Цикл while виконується, поки IN1 >= 1.
  2. Кожного разу, коли цикл виконується, значення IN1 виводиться.
  3. Після кожного виведення IN1-- зменшує значення на 1, врешті-решт завершуючи цикл, коли IN1 стає меншим за 1.

5. Покращений цикл For (For-Each цикл)

Логічні інструкції:

Start //Точка початку коду  
Print "Показати всі числа в масиві";  
ARRAY = [1, 2, 3, 4, 5];  
FOR EACH ELEMENT IN ARRAY {  
 Print ELEMENT;  
}

Код Java:

// Main - Точка початку виконання програми  
public class Main {  
 public static void main(String[] args) {  
 // Вивести повідомлення на екран  
 System.out.println("Показати всі числа в масиві");  

 // Ініціалізуємо масив чисел  
 int[] array = {1, 2, 3, 4, 5};  

 // Використовуємо покращений цикл for для виведення всіх елементів масиву  
 for (int element : array) {  
 // Вивести поточний елемент  
 System.out.println(element);  
 }  
 }  
}

Пояснення:

  1. Цикл for-each (for (int element : array)) проходить через всі елементи масиву array.
  2. Для кожної ітерації поточний елемент виводиться.
  3. Цикл автоматично проходить через всі елементи без необхідності відстежувати індекси.

Різниця між циклами While, For і Покращеним For

  • Цикл For:
    • Найкраще використовувати, коли кількість ітерацій відома заздалегідь.
    • Приклад: Лічити від 1 до 5.
  • Цикл While:
    • Найкраще використовувати, коли кількість ітерацій невідома, але цикл продовжується, поки умова є істинною.
    • Приклад: Лічити від 5 до 1.
  • Покращений цикл For:
    • Найкраще використовувати для ітерації через колекції (як масиви), коли потрібно отримувати кожен елемент по черзі.
    • Приклад: Виведення всіх чисел у масиві.

Чому використовувати If-Else та цикли в Java?

  • Оператор If-Else:
    • Допомагає приймати рішення на основі умов.
    • Приклад: Перевірка, чи є число більшим за 5.
  • Цикли:
    • Цикли корисні для виконання повторюваних завдань.
    • Використовуйте цикл for, коли ви знаєте, скільки разів потрібно пройти через цикл.
    • Використовуйте цикл while, коли кількість ітерацій невідома і ви просто хочете, щоб цикл продовжувався, поки умова істинна.
    • Використовуйте
      Це тому, що новий телефон "підходить" під очікування зарядного пристрою.

Таким самим чином, у програмуванні, якщо частина коду очікує загальний "тип" об'єкта, то будь-який більш специфічний "підтип" також має працювати без проблем. Якщо це не так, то це порушує принцип і може призвести до проблем, таких як неочікувана поведінка або помилки.

Приклад

Прямокутник і квадрат, що порушують принцип LSP:

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

 public int Area() => Width * Height;  
}  

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

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

Коли ми використовуємо Square замість Rectangle, поведінка стає непослідовною, оскільки встановлення одного виміру змінює інший.

Перероблено з урахуванням LSP

public interface IShape {  
 int Area();  
}  

public class Rectangle : IShape {  
 public int Width { get; set; }  
 public int Height { get; set; }  
 public int Area() => Width * Height;  
}  

public class Square : IShape {  
 public int Side { get; set; }  
 public int Area() => Side * Side;

4. Принцип сегрегації інтерфейсів (ISP)

Визначення: Клас не повинен бути змушений реалізовувати інтерфейси, які він не використовує.

Уявіть собі швейцарський багатофункціональний ніж. Хоча він має багато інструментів, ви не очікуєте, що пилочка для нігтів буде також працювати як викрутка. Кожен інструмент повинен виконувати свою конкретну задачу, без примусу робити щось інше.

У програмуванні це означає розподілення великих інтерфейсів на менші, щоб клас реалізовував тільки те, що йому дійсно потрібно.

Приклад

Інтерфейс з надмірною кількістю обов'язків:

public interface IWorker {  
 void Work();  
 void Eat();  
}  

public class Robot : IWorker {  
 public void Work() {  
 // Логіка роботи робота  
 }  
 public void Eat() {  
 throw new NotImplementedException();  
 }  
}

Перероблено з урахуванням ISP

Розділити інтерфейс на менші, більш орієнтовані інтерфейси:

public interface IWorker {  
 void Work();  
}  

public interface IEater {  
 void Eat();  
}  
public class Human : IWorker, IEater {  
 public void Work() {  
 // Логіка роботи людини  
 }  
 public void Eat() {  
 // Логіка харчування людини  
 }  
}  
public class Robot : IWorker {  
 public void Work() {  
 // Логіка роботи робота  
 }  
}

5. Принцип інверсії залежностей (DIP)

Визначення: Модулі високого рівня не повинні залежати від модулів низького рівня. Обидва повинні залежати від абстракцій.

Уявіть собі, що ви підключаєте лампу до розетки. Лампа не повинна знати, чи живиться вона від сонячних панелей або вітрових турбін. Вона покладається на розетку, щоб отримати необхідну енергію, незалежно від джерела. Розетка виступає як абстракція, забезпечуючи сумісність незалежно від постачальника енергії.

У програмуванні абстракції гарантують, що модулі високого рівня не залежать від специфічних деталей, що робить систему більш гнучкою і легкою для обслуговування.

Приклад

Клас, що безпосередньо залежить від низькорівневої реалізації:

public class FileLogger {  
 public void Log(string message) {  
 // Логування в файл  
 }  
}  

public class OrderService {  
 private readonly FileLogger _logger = new FileLogger();  
 public void PlaceOrder(Order order) {  
 _logger.Log("Замовлення зроблено.");  
 }

Перероблено з урахуванням DIP

Залежність від абстракцій замість конкретних реалізацій:

public interface ILogger {  
 void Log(string message);  
}  

public class FileLogger : ILogger {  
 public void Log(string message) {  
 // Логування в файл  
 }  
}  

public class OrderService {  
 private readonly ILogger _logger;  
 public OrderService(ILogger logger) {  
 _logger = logger;  
 }  
 public void PlaceOrder(Order order) {  
 _logger.Log("Замовлення зроблено.");  
 }  
}

Висновок

Принципи SOLID є потужними орієнтирами, які можуть значно покращити якість вашого коду.
Ці принципи сприяють тому, щоб розробники писали чисте, модульне та підтримуване програмне забезпечення, що робить ваші додатки легшими для тестування, розширення та налагодження.

Однак важливо зауважити, що в реальному розробницькому процесі бувають моменти, коли ми можемо не звертати увагу на ці принципи або знайти складнощі в їх застосуванні через обмеження, такі як жорсткі строки, старі кодові бази або динаміка роботи в команді. Хоча досягнення досконалості не завжди можливе, прагнення до включення цих принципів там, де це можливо, може принести значні довгострокові переваги.

Впровадження принципів SOLID у розробці .NET є практичним кроком до створення надійних та майбутньо орієнтованих додатків. Почніть з малого, застосовуйте один принцип за раз і спостерігайте, як ваш код перетворюється на більш чисту та підтримувану систему.

Перекладено з: From Confusion to Clarity: SOLID Principles with .NET examples

Leave a Reply

Your email address will not be published. Required fields are marked *