Для меня написание кода — сложная задача. Особенно, когда задач много, и кода тоже надо написать много. Мы можем обернуть наш ноутбук, написав коды в одном классе, но эффективно ли это? Таким образом, есть способы сделать процесс более эффективным и действенным. Применяя передовые методы программирования, такие как ООП и SOLID, вы можете писать более качественный код, который легче поддерживать, тестировать и изменять.

В этой статье я объясню на примере принципов ООП и SOLID, чтобы улучшить вашу игру кодирования! 🎯

✨ Принцип ОО

Первый — знаменитый принцип объектно-ориентированного программирования. Основанный на partech.com, принцип объектно-ориентированного программирования заключается в создании объектов, которые имеют как данные, так и функции. Фундаментальный принцип объектно-ориентированного программирования содержит четыре принципа: абстракцию, инкапсуляцию, наследование и полиморфизм.

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

№1. Абстракция

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

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

class Phone:
  function call()
  function send_message()
  function take_picture()

class Smartphone extends Phone:
  function access_internet()
  function play_music()

user = new Smartphone()
user.call()
user.send_message()
user.take_picture()

В этом примере псевдокода у нас есть класс Phone, определяющий базовые функции телефона, такие как вызов, отправка сообщения и фотографирование. Затем у нас есть класс Smartphone, который наследуется от Phone и добавляет дополнительные функции, такие как доступ в Интернет и воспроизведение музыки.

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

№ 2. Инкапсуляция

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

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

Вот пример псевдокода для иллюстрации инкапсуляции.

class Phone {
  private:
    batteryLevel; // encapsulated battery level
    isOn; // encapsulated on/off state

  public:
    Phone() {
      batteryLevel = 100;
      isOn = false;
    }

    void turnOn() {
      isOn = true;
      display("Phone is on!");
    }

    void turnOff() {
      isOn = false;
      display("Phone is off.");
    }

    void charge(int amount) {
      // code to charge
    }

    void makeCall(string number) {
      if (isOn && batteryLevel >= 10) {
        // code to make call
      } else if (!isOn) {
        display("Please turn on the phone before making a call.");
      } else {
        display("Cannot make call. Low battery level.");
      }
    }

    void display(string message) {
      // code to display message on phone screen
    }
};

В этом примере переменные battery_level и is_on являются закрытыми, что означает, что доступ к ним возможен только внутри самого класса Phone. Общедоступные методы, такие как TurnOn(), TurnOff(), Charge() и makeCall(), предоставляют пользователю возможность взаимодействовать с телефоном, не беспокоясь о внутренних деталях.

Например, пользователь может вызвать метод charge() для зарядки аккумулятора телефона, не зная, как внутри работает процесс зарядки. Метод charge() проверяет, включен или выключен телефон и находится ли уровень заряда батареи в пределах допустимого диапазона.

Точно так же пользователь может сделать вызов, вызвав метод make_call(), который проверяет, включен ли телефон и достаточен ли уровень заряда батареи, и отображает соответствующее сообщение на экране телефона.

№3. Наследование

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

Вот пример псевдокода для иллюстрации наследования.

class Phone {
    batteryCapacity;
    screenSize;
    brand;

    constructor(capacity, size, brand) {
        this.batteryCapacity = capacity;
        this.screenSize = size;
        this.brand = brand;
    }

    makeCall(number) {
        // Code to make a phone call
    }

    sendMessage(message) {
        // Code to send a text message
    }
}

class Smartphone extends Phone {
    operatingSystem;

    constructor(capacity, size, brand, os) {
        super(capacity, size, brand);
        this.operatingSystem = os;
    }

    takePicture() {
        // Code to take a picture using the smartphone camera
    }

    accessInternet() {
        // Code to access the internet using the smartphone
    }
}

// Example usage:
myPhone = new Smartphone(5000, 6, "Samsung", "Android");
myPhone.makeCall("1234567890");
myPhone.accessInternet();

В этом примере у нас есть класс Phone, который определяет основные свойства и методы телефона, такие как batteryCapacity, screenSize и makeCall(). Затем у нас есть класс Smartphone, который расширяет класс Phone и добавляет дополнительные свойства и методы, специфичные для смартфонов, такие как операционная система, takePicture() и accessInternet().

Используя наследование, мы можем избежать дублирования кода и вместо этого использовать существующий класс Phone для создания более конкретного класса Smartphone, который наследует все основные свойства и методы родительского класса.

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

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

Это принцип ООП. Надеюсь, вы понимаете, что я пытаюсь вам сказать 🥳.

✨ ТВЕРДЫЕ принципы

Наконец, это SOLID Principles! 🔥

Что именно это?

SOLID — это аббревиатура первых пяти принципов объектно-ориентированного проектирования (OOD), разработанных Робертом К. Мартином. Это аббревиатура пяти принципов, составляющих принципы SOLID: принцип единой ответственности, принцип открытости-закрытости, принцип замещения Лискова, принцип разделения интерфейса и принцип инверсии зависимостей.

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

Я дам вам пояснения и примеры в контексте животного 🐱, чтобы вы это поняли.

№1. Принцип единой ответственности

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

В контексте животных мы можем думать о классе, который представляет определенный тип животных, например, лев 🦁. Класс льва должен содержать только методы, относящиеся к поведению льва, такие как охота, рычание и движение.

class Lion {
  function hunt(prey) {
    // Code to hunt prey
  }

  function roar() {
    // Code to roar
  }

  function move() {
    // Code to move
  }
}

В этом примере у класса Lion есть единственная обязанность — представлять поведение льва. Методы в классе связаны с этой ответственностью и не включают никаких посторонних функций.

№ 2. Принцип открытого-закрытого

Принцип открытости-закрытости (OCP) гласит, что классы должны быть открыты для расширения, но закрыты для модификации. Другими словами, мы должны иметь возможность расширять поведение класса без изменения существующего кода.

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

class Mammal {
  function nurseYoung() {
    // Code to nurse young
  }
}

class Dog extends Mammal {
  function bark() {
    // Code to bark
  }
}

class Cat extends Mammal {
  function meow() {
    // Code to meow
  }
}

В этом примере класс Mammal закрыт для модификации, что означает, что мы не можем изменить существующее поведение класса. Однако мы можем расширить класс, создав новые подклассы, такие как Dog 🐶 и Cat 🐱, которые добавляют дополнительную функциональность без изменения существующего кода.

№3. Принцип замены Лисков

Принцип замещения Лискова (LSP) гласит, что объекты суперкласса должны заменяться объектами подкласса без ущерба для корректности программы. Другими словами, подкласс должен иметь возможность заменить свой родительский класс, не вызывая непредвиденного поведения.

В контексте животных мы можем думать о классе, который представляет определенный тип животных, например, птицу 🐦. Птица умеет летать, поэтому мы можем создать класс Bird с методом fly(). Однако некоторые птицы, такие как пингвины 🐧, не умеют летать. Основываясь на LSP, мы должны иметь возможность заменить класс Bird на класс Penguin, не вызывая неожиданного поведения.

class Bird {
  function fly() {
    // Code to fly
  }
}

class Penguin extends Bird {
  function swim() {
    // Code to swim
  }
}

function makeBirdFly(Bird $bird) {
  bird.fly();
}

eagle = new Bird();
penguin = new Penguin();

makeBirdFly(eagle); // This will work fine
makeBirdFly(penguin); // This will cause an error since penguins can't fly

В этом примере у нас есть класс Bird с методом fly(), который представляет поведение обычной птицы. Затем у нас есть класс Penguin, который расширяет класс Bird, но не реализует метод fly(), поскольку пингвины не умеют летать.

У нас должна быть возможность передать объект Penguin функции, которая ожидает объект Bird, но это вызовет ошибку, поскольку пингвины не могут летать.

№ 4. Принцип разделения интерфейса

От класса не следует требовать реализации методов, которые ему не нужны. Это ядро ​​I в SOLID.

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

// Animal interface
interface Animal {
  void eat();
  void sleep();
  void move();
}

// FlyingAnimal interface, which extends Animal interface
interface FlyingAnimal extends Animal {
  void fly();
}

// SwimmableAnimal interface, which extends Animal interface
interface SwimmableAnimal extends Animal {
  void swim();
}

// Bird class, which implements FlyingAnimal interface
class Bird implements FlyingAnimal {
  void eat() {
    // code to eat food
  }

  void sleep() {
    // code to sleep
  }

  void move() {
    // code to move around
  }

  void fly() {
    // code to fly
  }
}

// Fish class, which implements SwimmableAnimal interface
class Fish implements SwimmableAnimal {
  void eat() {
    // code to eat food
  }

  void sleep() {
    // code to sleep
  }

  void move() {
    // code to move around
  }

  void swim() {
    // code to swim
  }
}

В этом примере у нас есть интерфейс Animal, который определяет базовое поведение, которое должно быть у всех животных: еда, сон и движение. У нас также есть еще два специализированных интерфейса, FlyingAnimal и SwimmableAnima. Эти два расширения расширяют интерфейс Animal и определяют дополнительные типы поведения, специфичные для летающих и плавающих животных.

Класс Bird реализует интерфейс FlyingAnimal, что означает, что он должен реализовывать все методы интерфейса Animal и метод fly(), определенные интерфейсом FlyingAnimal.

Точно так же класс Fish реализует интерфейс SwimmableAnimal, что означает, что он должен реализовывать все методы интерфейса Animal и метод swim(), определенный интерфейсом SwimmableAnimal.

№ 5. Принцип инверсии зависимости

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

interface Animal {
  function speak();
}

class Dog implements Animal {
  function speak() {
    print("Woof!");
  }
}

class Cat implements Animal {
  function speak() {
    print("Meow!");
  }
}

class AnimalSound {
  private Animal animal;

  constructor(animal) {
    this.animal = animal;
  }

  function makeSound() {
    animal.speak();
  }
}

// Example usage:
let dog = new Dog();
let cat = new Cat();

let dogSound = new AnimalSound(dog);
let catSound = new AnimalSound(cat);

dogSound.makeSound(); // Output: "Woof!"
catSound.makeSound(); // Output: "Meow!"

Например, предположим, что у нас есть интерфейс Animal с методом speak(). У нас также есть классы Dog и Cat, которые реализуют метод speak(). Если мы хотим использовать метод speak() в другом классе, например в классе AnimalSound, мы можем передать объект Animal как зависимость через его конструктор. Это позволяет нам легко переключать тип животного без изменения класса AnimalSound.

В целом цель принципов SOLID — уменьшить количество зависимостей, чтобы инженеры могли изменять одну область программного обеспечения, не затрагивая другие (bmc.com). Следуя этим принципам, мы можем способствовать лучшему дизайну, снижать риск критических изменений и повышать общее качество наших программных систем.

✨ Заключение

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

Фундаментальные принципы ООП (абстракция, инкапсуляция, наследование и полиморфизм) позволяют разработчикам создавать эффективный и организованный код.

В то время как принципы SOLID (принцип единой ответственности, принцип открытости-закрытости, принцип замещения Лискова, принцип разделения интерфейса и принцип инверсии зависимостей) помогают разработчикам создавать программное обеспечение, которое легко модифицировать и расширять. Следуя этим принципам, мы можем сократить количество зависимостей, улучшить дизайн, снизить риск критических изменений и улучшить общее качество наших программных систем.

И это закрутка, спасибо! 💖

Я буду признателен за ваши отзывы 💬 и аплодисменты 👏.

Если вы хотите сотрудничать, не стесняйтесь обращаться ко мне по адресу [email protected] или через Linkedin

Рекомендации









https://www.indeed.com/career-advice/career-development/what-is-object-Oriented-programmingg