Вероятно сте чували фразата „повторна употреба на код“ няколко пъти в кариерата си за разработка на софтуер. Като софтуерни инженери, ние често повтаряме едни и същи методи/функции в множество класове, докато следваме обектно-ориентираното програмиране. Но какво точно е обектно-ориентираното програмиране?

Обектно-ориентираното програмиране (ООП) е програмна парадигма, която се основава на концепцията за обекти. Той разчита на класове и обекти, за да структурира програма в прости, многократно използвани части. Класовете се използват за създаване на отделни екземпляри на обекти.

Дефиницията по-горе спомена „класове за многократна употреба“, поради което характеристиките са свързани с обектно-ориентираното програмиране (ООП). Следването на принципите на ООП помага да направите кода си по-прост и по-структуриран. Много езици за програмиране, включително JavaScript, PHP и Python…, поддържат ООП.

Споменахме думата „класове“ доста пъти в тази статия и разбирам, че може да сте объркани какво представляват те. Мога да ви уверя, че те не са същите като класовете, които посещаваме 😏. Класовете са абстрактни чертежи или общи очертания за създаване на конкретни обекти или концепции. Те представляват широки категории, които споделят едни и същи атрибути. Например, нека вземем различни марки автомобили, като BMW и Audi. Те споделят сходни характеристики, като цвят и гуми. Тези класове определят атрибутите, които ще имат, но не и стойностите

Класовете също имат методи, които са достъпни за тези обекти. Тези функции са дефинирани в рамките на класовете, които изпълняват някои действия, които могат да помогнат на тези конкретни обекти. Пример за това е необходимостта от пребоядисване на тези марки автомобили, споменати по-рано, пребоядисването може да бъде метод, който променя цветовия атрибут на автомобилите. Обектите могат да имат уникални стойности за свойствата, дефинирани в класа. Използвайки нашия пример по-рано, клас Car ще съдържа свойства като марка, модел и цвят.

Достатъчно въведение в обектно-ориентираното програмиране, нека сега да поговорим за характеристики. Дублиращите се кодове са много често срещани в множество класове, с Traits можете лесно да споделяте методи и свойства между класовете, точно както споделяте любимата си качулка с вашия BFF. Чертите са може би най-доброто решение за СУХИЯ принцип (не се повтаряй). Нека ви въведа в света на Чертите.

Както казахме по-рано, Traits ви позволяват да споделяте методи и свойства с множество класове. Ако една черта подобрява даден клас, тогава всички подкласове също се подобряват. Обикновено, когато използваме наследяване, един клас може да наследява само от един родителски клас. С нарастването на кодовата база това може да се усложни, особено когато множество класове имат подобни функции. Но с черти, един клас може да включва толкова характеристики, колкото са му необходими, което от своя страна прави повторното използване на кода по-гъвкаво.

Знам, че ще се чудите как мога да използвам черти в реалния свят. Да предположим, че имаме нужда от няколко класа, за да направим някакво регистриране. Това е много дублиране на код и следенето на всички тези изявления в журнала може да бъде истинско главоболие. Но с чертите можете просто да извлечете целия този общ код за регистриране в едно чисто и подредено парче код. След това можете да включите тази характеристика във всеки от класовете, които се нуждаят от нея, като ви спестява необходимостта да копирате и поставяте код навсякъде. Сега поддържането и актуализирането на вашата функционалност за регистриране е лесно като пай.

Нека да разгледаме няколко реални примера за черти в JavaScript, Python и PHP:

JavaScript

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

// Logging trait
const Logging = {
  logToConsole(message) {
    console.log(message);
  },
  logToFile(message) {
    // code to log to a file
  },
  logToDb(message) {
    // code to log to a database
  },
};
// User class
class User {
  login() {
    // code to handle user login
    this.logToConsole(`User logged in: ${this.name}`);
  }
}
// Extend the User class with the Logging trait
Object.assign(User.prototype, Logging);
// Product class
class Product {
  purchase() {
    // code to handle product purchase
    this.logToConsole(`Product purchased: ${this.name}`);
  }
}
// Extend the Product class with the Logging trait
Object.assign(Product.prototype, Logging);
// Page class
class Page {
  view() {
    // code to handle page view
    this.logToConsole(`Page viewed: ${this.url}`);
  }
}
// Extend the Page class with the Logging trait
Object.assign(Page.prototype, Logging);

PHP

Да приемем, че създавате уебсайт за електронна търговия, който трябва да регистрира различни събития, като прегледи на продукти, добавяне в количката и плащане. Може да се използва характеристика, наречена „Регистриране“, която съдържа методи за регистриране на събития в различни канали, като файл или база данни. Вече можем да включим характеристиката „Регистриране“ в съответните класове, като класовете „Продукт“, „Количка“ и „Плащане“, така че всички те да могат да използват една и съща функция за регистриране.

// Logging trait
trait Logging {
  public function logToFile($message) {
    // code to log to a file
  }
  public function logToDb($message) {
    // code to log to a database
  }
}
// Product class
class Product {
  use Logging;
  public function view() {
    // code to handle product view
    $this->logToFile("Product viewed: " . $this->name);
  }
}
// Cart class
class Cart {
  use Logging;
  public function add() {
    // code to handle add to cart
    $this->logToFile("Product added to cart: " . $this->product->name);
  }
}
// Checkout class
class Checkout {
  use Logging;
  public function checkout() {
    // code to handle checkout
    $this->logToFile("Order checked out: " . $this->order->id);
  }
}

Python

Да приемем, че създавате уебсайт за електронна търговия, който трябва да регистрира различни събития, като прегледи на продукти, добавяне в количката и плащане. Може да се използва черта или миксин (в python), наречен „Регистриране“, който съдържа методи за регистриране на събития в различни канали, като файл или база данни. Вече можем да включим характеристиката „Регистриране“ в съответните класове, като класовете „Продукт“, „Количка“ и „Плащане“, така че всички те да могат да използват една и съща функция за регистриране.

# Logging mixin
class Logging:
    def log_to_file(self, message):
        # code to log to a file
        pass
def log_to_db(self, message):
        # code to log to a database
        pass
# Product class
class Product(Logging):
    def view(self):
        # code to handle product view
        self.log_to_file("Product viewed: " + self.name)
# Cart class
class Cart(Logging):
    def add(self):
        # code to handle add to cart
        self.log_to_file("Product added to cart: " + self.product.name)
# Checkout class
class Checkout(Logging):
    def checkout(self):
        # code to handle checkout
        self.log_to_file("Order checked out: " + str(self.order.id))

Сега нека обясним кодовете, написани по-горе: Кодът настройва регистрационно събитие в нашето уеб приложение. Процесът на регистриране е разделен на отделна „характеристика“, наречена регистриране. За JavaScript има допълнителен метод, който е logToConsole(), докато PHP и Python имат по 2 метода, главно Log to File и Log to DB (база данни).

След това характеристиката се използва в три различни класа Product, Cart и Checkout за PHP и Python. За Javascript използвахме характеристиката в потребител, продукт и страница. Всеки от тези класове има метод, използван за задействане на събитие в регистрационния файл, като например когато се преглежда страница или когато се изважда поръчка. Когато тези методи бъдат извикани, регистрационното събитие се предава на съответния метод за регистриране от функцията за регистриране.

По принцип характеристиката Logging предоставя стандартизиран начин за различна част от приложението да регистрира събития, от горния пример ние написахме метода Logging само веднъж и той беше използван в цялото приложение. Характеристиките направиха управлението на тази функционалност по-лесно, като по този начин увеличиха повторното използване на кода.

В заключение, използването на характеристики в обектно-ориентираното програмиране е като да имате кутия с инструменти, пълна с удобни функции и методи, от които се нуждаете, за да свършите работата. С характеристиките можете да избегнете главоболието от дублиращи се кодове и на свой ред да се придържате към принципа DRY. Така че, следващия път, когато създавате приложение, не забравяйте да използвате мощния инструментариум, наречен Traits, защото кой не обича да увеличи максимално повторното използване на кода? Или искате да останете копи-пейст чудовище?