Część 2 – WordPress i programowanie obiektowe: przykład ze świata rzeczywistego
Opublikowany: 2021-07-29W naszym Przeglądzie WordPressa i programowania zorientowanego obiektowo omówiliśmy teorię programowania zorientowanego obiektowo (OOP) i czego można się spodziewać podczas korzystania z niego.
Zanim przejdziemy do bardziej szczegółowych przykładów kodowania przy użyciu OOP, w tym artykule postaramy się wyjaśnić, w jaki sposób można podejść do scenariusza w świecie rzeczywistym z innym nastawieniem wymaganym do OOP i jak jest to analizowane za pomocą obiektów i klas.
Scenariusz z prawdziwego życia: wysyłanie wiadomości SMS
To bardziej przypomina scenariusz „przeszłego” życia, ponieważ SMS-y są obecnie coraz rzadziej używane, ale jak zobaczysz, jest powód, dla którego używamy tego jako przykładu!
Załóżmy, że masz urządzenie mobilne i chcesz wysłać wiadomość tekstową do jednego ze swoich kontaktów. Zachowując przykład tak prosty, jak to tylko możliwe, kolejność działań wyglądałaby następująco:
- Przygotuj wiadomość
- Wybierz jeden ze swoich kontaktów i dodaj jako odbiorcę
- Wyślij wiadomość
Spróbujmy więc zwizualizować kroki, które należy wykonać, aby wysłać wiadomość:
Dodaliśmy bardziej szczegółowe opisy akcji, ale mniej więcej wszystko, co robisz, to 3 podstawowe kroki. Przygotowujesz wiadomość w edytorze urządzenia, wybierasz odbiorcę z kontaktów, a następnie wysyłasz wiadomość. I gotowe! Twoja wiadomość została wysłana.
Teraz, gdybyśmy mieli reprezentować w kodzie aplikację, która wysyła wiadomość SMS, powinniśmy przeanalizować, którą trasą najlepiej podążać; podejście proceduralne lub OOP.
Aplikacja z podejściem proceduralnym
Jeśli jesteś programistą wtyczek do WordPressa, najprawdopodobniej znasz programowanie proceduralne .
Jak opisaliśmy wcześniej, programowanie proceduralne jest rodzajem programowania imperatywnego, w którym nasze programy składają się z jednej lub więcej procedur. Tak więc, jako programista, dzielisz swoją wtyczkę na kilka zmiennych , które przechowują twoje dane i funkcje , które operują na danych.
W naszym przykładzie powyżej z wiadomością SMS wykonałbyś serię działań, które doprowadziłyby do pożądanego rezultatu. Jak już zapewne zgadłeś, będziesz mieć na przykład zmienną przechowującą treść wiadomości, funkcję z parametrem $contact
, która zwraca numer telefonu, a na końcu funkcję wysyłającą wiadomość. W kodzie wyglądałoby to tak:
function get_phone_number( $contact ) { // Code that finds the contact's number in the list of contacts return $phone_number; } function send_sms( $contact, $message ) { $phone_number = get_phone_number( $contact ); // Code that sends the message to this number print "Message Sent!"; }
I użyłbyś tego w ten sposób:
$text = "Hello John"; function send_message( "John Doe", $text );
Tak więc wykonałbyś szereg zadań, które doprowadzą Cię do pożądanego rezultatu.
W tym bardzo prostym przykładzie, który ma ograniczone i bardzo specyficzne wymagania, nie ma żadnego powodu, aby w ogóle rozważać używanie OOP. Programowanie proceduralne jest więcej niż wystarczające, aby osiągnąć swój cel. Jeśli jednak zastanowisz się nad pewnymi scenariuszami rozwoju tej aplikacji w przyszłości, możesz zdać sobie sprawę, że na dłuższą metę możesz mieć problemy z skalowalnością. Poniżej postaramy się wyjaśnić dlaczego.
Rozszerzanie aplikacji o podejście proceduralne
Załóżmy, że chcesz ulepszyć tę aplikację i zapewnić możliwość wysyłania innych rodzajów wiadomości, na przykład wiadomości e-mail. Funkcja dostarczająca wiadomość byłaby inna w każdym przypadku.
Wysyłając wiadomość e-mail, potrzebujesz adresu e-mail kontaktu, a nie numeru telefonu. Oprócz tego będziemy musieli dodać parametr w końcowej funkcji send_message()
, który będzie odpowiadał rodzajowi używanej technologii; e-mail lub SMS.
Odpowiedni kod może wyglądać mniej więcej tak:
function get_phone_number( $contact ) { // Code that finds the contact's number return $phone_number; } function get_email_address( $contact ) { // Code that finds the contact's email address return $email_address; } function send_sms( $contact, $message ) { $phone_number = get_phone_number( $contact ); // Code that sends the message to this number print "SMS Sent!"; } function send_email( $contact, $message ) { $email_address = get_email_address( $contact ); // Code that sends the email to this number print "Email Sent!"; } function send_message( $contact, $message, $technology ) { if ( $technology == "SMS") { send_sms( $phone_number, $message ); } else if ( $technology == "Email") { send_email( $email_address, $message ); } }
Nie jest więc tak, że nie można tego zrealizować za pomocą podejścia proceduralnego. Ale jeśli jesteś doświadczonym programistą, prawdopodobnie już zrozumiałeś, jak może to stać się bałaganem w przyszłości.
Wady podejścia proceduralnego
Co by było, gdybyśmy mieli wiele rodzajów wiadomości? Stwierdzenia if
stałyby się irytująco duże. A co najważniejsze, co by było, gdybyś miał funkcje korzystające z funkcji send_message()
? W takim przypadku należałoby dodać parametr $technology
również we wszystkich tych funkcjach.
W miarę rozwoju kodu funkcje będą wszechobecne, co oznacza, że zaczniesz kopiować/wklejać fragmenty kodu (nigdy nie jest to pożądane), a wprowadzenie niewielkiej zmiany w funkcji może spowodować uszkodzenie kilku innych funkcji. Wszyscy tam byliśmy. Chciałbyś tego uniknąć i móc łatwo dodawać funkcje do kodu bez zbytniego ingerowania w strukturę.
Programowanie obiektowe (lub OOP) to paradygmat programowania, który próbuje rozwiązać ten problem, pozwalając nam na zorganizowanie naszej wtyczki w małe, wielokrotnego użytku fragmenty kodu, zwane klasami . Jak opisaliśmy w naszym artykule Przegląd OOP, klasa jest w zasadzie szablonem, którego używamy do tworzenia indywidualnych wystąpień klasy, zwanych obiektami .
Obiekt zawiera dane i kod. Nadal mamy zmienne, które mogą przechowywać informacje, zwane właściwościami . Oraz procedury operujące na danych, zwane metodami .
Aplikacja z podejściem OOP
Teraz przeanalizujmy ten sam scenariusz, co powyżej, z podejściem OOP.
Najpierw zdefiniujemy, jakie mamy tutaj obiekty, jakie cechy ma każdy z nich i jakie czynności wykonują. Charakterystyki są tym, co później będzie naszymi właściwościami, a działania będą naszymi funkcjami lub metodami, jak są nazywane w OOP.
Zastanówmy się, co mamy w pierwszym scenariuszu wysyłania SMS-a w najprostszy możliwy sposób. Istnieje urządzenie, które posiada interfejs, którego używamy do wysyłania wiadomości SMS. Mamy treść wiadomości, wybieramy kontakt jako odbiorcę i wreszcie wiadomość.
<?php /** * Plugin Name: Send Message */ interface MessagingCapable { public function send_message( $contact, $message ); } class Phone implements MessagingCapable { public function send_message( $contact, $message ) { print "You sent" . $message ; } } function say_hi( MessagingCapable $device, $contact, $message ) { $device->send_message( $contact, $message ); }
Deklarujemy klasę Phone
, która implementuje interfejs MessagingCapable
. Musimy więc zaimplementować wszystkie zadeklarowane w nim metody. Funkcja say_hi()
wymaga 3 parametrów:
- Urządzenie obsługujące wiadomości
- Kontakt
- Wiadomość
Aby faktycznie wysłać wiadomość, używamy tej funkcji w następujący sposób:
$phone = new Phone(); say_hi( $phone, "John Doe", "Hello John" );
Zasadniczo tworzymy obiekt, tworząc instancję klasy Phone i przekazując treść kontaktu i wiadomości. To dałoby:
You sent "Hello John"
Zademonstrowaliśmy ten prosty scenariusz wysyłania wiadomości tekstowej za pomocą zajęć. W następnej sekcji zobaczymy, w jaki sposób możemy rozszerzyć możliwości aplikacji zgodnie z podejściem OOP i podczas skalowania w górę zbadamy, gdzie funkcje OOP odgrywają swoją rolę, a także korzyści płynące z zastosowania tej techniki.
Rozszerzenie Aplikacji o podejście OOP
Dodajmy również możliwość wysyłania e-maili, tak jak robiliśmy to wcześniej proceduralnie.
Niezależnie od urządzenia, najlepiej byłoby użyć funkcji say_hi()
w ten sam sposób. Spójrz na poniższy kod:
<?php /** * Plugin Name: Send Message */ interface MessagingCapable { public function send_message( $contact, $message ); } class Phone implements MessagingCapable { public function send_message( $contact, $message ) { print ('You sent a "' . $message . '" SMS to ' . $contact ); } } class Computer implements MessagingCapable { public function send_message( $contact, $message ) { print ('You sent a "' . $message . '" email to ' . $contact ); } } function say_hi( MessagingCapable $device, $contact, $message ) { $device->send_message( $contact, $message ); }
Kiedy używamy tego fragmentu kodu, podnosimy urządzenie mobilne, aby wysłać SMS-a, a komputer, aby wysłać e-mail. Chcielibyśmy albo:
say_hi ( new Phone(), "John Doe", "Hello John" );
lub:
say_hi ( new Computer(), "John Doe", "Hello John" );
to spowoduje, że You sent a "Hello John" SMS to John Doe
i You sent a "Hello John" email to John Doe
.
Tutaj już zaczynamy wykrywać niektóre funkcje OOP. Wprowadziliśmy interfejsy za pomocą interfejsu MessagingCapable
.
Interfejs deklaruje zestaw metod, które muszą być zaimplementowane przez klasę bez definiowania sposobu implementacji tych metod. Wszystkie metody zadeklarowane w interfejsie muszą być publiczne.
PHP nie obsługuje wielokrotnego dziedziczenia, co oznacza, że klasa nie może dziedziczyć właściwości/metod wielu klas nadrzędnych.
Chociaż może rozszerzać tylko jedną klasę, może implementować wiele interfejsów.
Używanie telefonu do wysyłania wiadomości różni się od korzystania z komputera. Instancje różnych klas zachowują się inaczej, gdy zostaną poproszone o wykonanie tej samej akcji (np. send_message()
). To jest przykład polimorfizmu. Jeśli później utworzymy nowe urządzenie, nie będziemy musieli modyfikować naszego kodu, aby je dostosować, o ile wszystkie mają ten sam interfejs.
Chcielibyśmy również zaznaczyć, że już widzimy różnicę w czytelności. Sposób, w jaki w końcu używamy tego skryptu, po prostu kodując:
say_hi( new Computer(), "John", "Hi" );
Jest to całkowicie proste dla każdego programisty, który pracuje nad projektem. I oczywiście im bardziej złożona wtyczka, tym bardziej oczywiste staje się, jak bardzo jest to pomocne, zwłaszcza podczas pracy w zespole.
Aby lepiej wyjaśnić, jak łatwo jest rozszerzyć swoją wtyczkę w programowaniu zorientowanym obiektowo, spróbujmy dodać trochę więcej funkcji.
Dodawanie większej funkcjonalności
Jeśli chcemy dodać możliwość przeglądania Internetu, dodamy po prostu dodatkowy interfejs dla dowolnego urządzenia, które mogłoby odpowiedzieć na tę możliwość, na przykład komputer.
interface InternetBrowsingCapable { public function visit_website( $url ); }
Implementacja tego interfejsu będzie zakodowana w następujący sposób:
class Computer implements MessagingCapable, InternetBrowsingCapable { public function send_message( $contact, $message ) { print ('You sent a "' . $message . '" email to ' . $contact ); } public function visit_website( $url ) { print ('You visited "' . $url ); } }
W obecnej klasie Computer właśnie dodaliśmy dodatkowy interfejs do zaimplementowania, ponieważ komputer może wysyłać wiadomości i przeglądać Internet oraz visit_website( $url )
.
UWAGA: Oczywiście, ponieważ odwiedzenie adresu URL jest całkowicie nieistotne w przypadku funkcji say_hi()
, wprowadzimy również nową funkcję, na przykład:
function visit_url( InternetBrowsingCapable $device, $url ) { $device->visit_website( $url ); }
I to wszystko! W przypadku każdego urządzenia, które może odwiedzić adres URL, możemy użyć tej funkcji, tak jak zrobiliśmy to z komputerem. Nie ma obaw, że zepsujesz resztę funkcjonalności. Pokazuje to skalowalność dostępną podczas korzystania z OOP w porównaniu z programowaniem proceduralnym.
Dodajmy urządzenie typu smartfon, aby zademonstrować kilka dodatkowych funkcji. Oto cały kod, z dodatkiem klasy smartfona, dzięki czemu można mieć lepszy obraz tego, co się dzieje:
<?php /* * Plugin Name: Communication Plugin */ interface MessagingCapable { public function send_message( $contact, $message ); } interface InternetBrowsingCapable { public function visit_website( $url ); } class Phone implements MessagingCapable { public function send_message( $contact, $message ) { print 'You sent a "' . $message . '" SMS to ' . $contact; } } class Computer implements MessagingCapable, InternetBrowsingCapable { public function send_message( $contact, $message ) { print 'You sent a "' . $message . '" email to ' . $contact; } public function visit_website( $url ) { print 'You visited "' . $url; } } class Smartphone extends Phone implements InternetBrowsingCapable { public function visit_website( $url ) { print 'You visited "' . $url; } public function send_message( $contact, $message ) { parent::send_message( $contact, $message ); print ' from your smartphone'; } } function say_hi( MessagingCapable $device, $contact, $message ) { $device->send_message( $contact, $message ); } function visit_url( InternetBrowsingCapable $device, $url ) { $device->visit_website( $url ); }
Klasa Smartphone rozszerza klasę nadrzędną Phone i implementuje interfejs InternetBrowsingCapable
. Oznacza to, że może wysłać wiadomość i odwiedzić adres URL. Tutaj wykrywamy funkcję dziedziczenia. Innymi słowy, mamy hierarchię klas, klasę nadrzędną (Telefon) i podklasę (Smartfon).
Tak więc obiekt Smartphone dziedziczy wszystkie właściwości i zachowania nadrzędnej klasy Phone. W ten sposób wewnątrz klasy potomnej możemy dodać metodę lub przesłonić metodę klasy nadrzędnej, tak jak zrobiliśmy to z send_message()
w klasie Smartphone. Zrobiliśmy to, aby zmienić wyjście. Moglibyśmy całkowicie zignorować tę metodę i użyć funkcji send_message()
klasy nadrzędnej w takiej postaci, w jakiej jest.
Możesz sam wypróbować kod, wklejając go w bloku kodu do tego wspaniałego narzędzia online PHP. Pod kodem wypróbuj dowolny z tych wierszy kodu i zobacz różne wyniki.
say_hi ( new Phone(), "John Doe", "Hello John" ); say_hi ( new Computer(), "John Doe", "Hello John" ); say_hi ( new Smartphone(), "John Doe", "Hello John" ); visit_url ( new Smartphone(), "https://www.pressidium.com" ); visit_url ( new Computer(), "https://www.pressidium.com" );
Aby jeszcze lepiej zrozumieć całą koncepcję, spójrz na diagram klas powyższego kodu.
Jak pokazano powyżej, projektując relacje między klasami, nie uwzględniamy elementów wspólnych w klasie potomnej. Ponadto nie zapomnij zwrócić uwagi w przewodniku po lewej stronie, aby zidentyfikować relacje i widoczność ich właściwości i metod.
Jeśli chcesz zobaczyć, jak działa funkcja enkapsulacji, spróbuj dołączyć klasę Contact do dowolnego z powyższych przykładowych skryptów, które dostarczyliśmy. Klasa wyglądałaby tak:
class Contact { private $name; private $phone_number; private $email_address; public function __construct( $name, $phone_number, $email_address ) { $this->name = $name; $this->phone_number = $phone_number; $this->email_address = $email_address; } public function get_name() { return $this->name; } public function get_phone_number() { return $this->phone_number; } public function get_email_address() { return $this->email_address; } }
Metoda __construct()
z założenia jest wywoływana automatycznie po utworzeniu obiektu. Teraz, gdy tworzymy instancję klasy Contact, jej konstruktor zostaje wywołany i ustawia wartości jej prywatnych właściwości. Następnie używamy naszych „getterów”, czyli metod publicznych get_name()
, get_phone_number()
i get_email_address()
, aby pobrać te wartości.
Enkapsulacja polega na łączeniu danych z metodami, które operują na danych, jednocześnie ograniczając bezpośredni dostęp, zapobiegając ujawnieniu ukrytych szczegółów implementacji.
Wniosek
Mam nadzieję, że ten artykuł pomógł ci zrozumieć programowanie zorientowane obiektowo w bardziej praktyczny sposób. Programowanie OOP naprawdę ułatwia rozbudowę aplikacji w przyszłości, jeśli będzie to konieczne, dzięki przejrzystości i możliwości ponownego wykorzystania.
Ponadto wtyczka korzystająca z OOP będzie szybsza i łatwiejsza do wykonania. Dzieje się tak, ponieważ metody, które są wspólne dla wszystkich obiektów danej klasy, zużywają pamięć tylko raz, podczas ich deklaracji.
Bezpieczeństwo jest również poprawione dzięki enkapsulacji. Z drugiej strony w programowaniu proceduralnym wszystkie dane są globalne, co oznacza, że dostęp jest możliwy z dowolnego miejsca.
W wyniku powyższego utrzymanie kodu, produktywność, skalowalność i rozwiązywanie problemów również stają się znacznie łatwiejsze dla Ciebie i Twojego zespołu.
W kolejnych artykułach z tej serii zobaczymy, jak działa ten styl programowania, stosując go do wtyczki WordPress. W szczególności utworzymy kopię wtyczki Limit Login Attempts w wersji 1.7.1 stworzonej przez Johana Eenfeldta, ale przekonwertowanej przy użyciu podejścia obiektowego w jak największym stopniu.
Podczas tego procesu podzielimy przepływ wtyczek i ustalimy wymagania. Idąc dalej, wypróbujemy nasze pierwsze przemyślenia na temat projektu wtyczki, a na etapie implementacji napiszemy kod. Podczas procesu wdrożenia dokonamy pewnych zmian i przeprojektujemy, jeśli to konieczne, w celu uzyskania pożądanych rezultatów.
Nie będziemy jednak wchodzić w szczegóły dotyczące wszystkich części kodu. Zamiast tego chcielibyśmy skupić się na udostępnianiu sposobu, w jaki wtyczki są budowane w sposób obiektowy. Jesteśmy pewni, że po przeczytaniu tej serii artykułów możesz bardzo dobrze stworzyć własną wtyczkę OOP.
Kliknij tutaj, aby przeczytać część 3 w naszej serii programowania zorientowanego na obiekt
Zobacz też
- WordPress i programowanie obiektowe – przegląd