บทนำสู่รูปแบบการออกแบบใน PHP (และใช้ประโยชน์จากมันใน Drupal)

เผยแพร่แล้ว: 2022-07-05

มีคนเคยกล่าวไว้ว่า - โค้ดเดอร์ที่ดี ใช้ซ้ำได้ดีเยี่ยม
นักพัฒนามักจะพบว่าตัวเองกำลังแก้ปัญหาประเภทเดียวกันซ้ำแล้วซ้ำเล่า การนำโค้ดกลับมาใช้ใหม่ถือเป็นจอกศักดิ์สิทธิ์ของการพัฒนาซอฟต์แวร์อย่างแท้จริง นอกจากนี้ ใครที่ไม่ชอบอ่าน (และเขียน) โค้ดที่มีโครงสร้างดี? Enter - รูปแบบการออกแบบใน PHP

รูปแบบการออกแบบ PHP ได้รับการพิสูจน์แล้วว่ามีประโยชน์อย่างยิ่งต่อนักพัฒนาและเป็นตัวแก้ปัญหาที่ยิ่งใหญ่ การปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเป็นสิ่งสำคัญในการเขียนโค้ดที่มีประสิทธิภาพ รูปแบบการออกแบบ PHP เป็นแนวคิดการเขียนโปรแกรมเชิงวัตถุ (OOP) ซึ่งตอนนี้ใช้ในโครงการ Drupal 9 ด้วย ด้วยการนำแนวคิด PHP และ OOP ที่ทันสมัยของ Drupal มาใช้ตั้งแต่เวอร์ชัน 8 ทำให้รูปแบบการออกแบบสามารถใช้ประโยชน์ได้สำหรับการเขียนโปรแกรมที่สะอาดขึ้นและมีประสิทธิภาพมากขึ้น ในบทความนี้ เราจะพูดถึงรูปแบบการออกแบบที่ใช้กันทั่วไปใน PHP และวิธีการใช้รูปแบบ เช่น การแทรกการพึ่งพาใน Drupal

ยังอยู่ใน Drupal 7? อ่านบทความนี้เพื่อค้นหารายการตรวจสอบที่มีประโยชน์ซึ่งจะช่วยให้คุณเตรียมพร้อมสำหรับการโยกย้าย Drupal 9

รูปแบบการออกแบบใน PHP

รูปแบบการออกแบบใน PHP คืออะไร?

ในงานวิศวกรรมซอฟต์แวร์ Design Pattern เป็นวิธีแก้ปัญหาทั่วไปที่ทำซ้ำได้สำหรับปัญหาที่เกิดขึ้นโดยทั่วไปในการออกแบบซอฟต์แวร์ การออกแบบเชิงวัตถุที่ดีควร นำมาใช้ใหม่ บำรุงรักษา ได้ และ ขยายได้ และรูปแบบการออกแบบใน PHP อาจมีประโยชน์มากในการทำเช่นนั้น ไม่เพียงแต่ช่วยในการแก้ปัญหา แต่ยังหมายถึงวิธีที่ดีที่สุดในการจัดการกับความท้าทายทั่วไป

เหตุใดจึงต้องใช้รูปแบบการออกแบบ PHP

ประโยชน์ที่สำคัญที่สุดบางประการของการนำรูปแบบการออกแบบไปใช้ใน PHP ได้แก่:

  • รูปแบบการออกแบบ PHP ช่วยแก้ปัญหาที่เกิดซ้ำๆ ระหว่างการพัฒนา
  • การใช้รูปแบบการออกแบบใน PHP ทำให้การสื่อสารระหว่างนักออกแบบและนักพัฒนามีประสิทธิภาพมากขึ้น
  • คุณสามารถมั่นใจได้ว่านักพัฒนาคนอื่นๆ จะเข้าใจโค้ดของคุณเนื่องจากเป็นไปตามรูปแบบการออกแบบ
  • การปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดจะช่วยสร้างแอปพลิเคชันที่มีประสิทธิภาพยิ่งขึ้น
  • ช่วยให้พัฒนาได้เร็วและง่ายขึ้น

รูปแบบการออกแบบที่ใช้กันอย่างแพร่หลายใน PHP

Design Patterns สามารถใช้ในสถานการณ์ต่างๆ เพื่อแก้ปัญหาที่คล้ายคลึงกัน มีรูปแบบการออกแบบมากกว่า 30 รูปแบบที่สามารถแบ่งออกกว้างๆ ได้เป็นสามประเภท - รูปแบบการสร้าง โครงสร้าง และพฤติกรรม

รูปแบบการสร้างสรรค์: รูป แบบการออกแบบที่ใช้ในกลไกการสร้างวัตถุ เพื่อสร้างวัตถุที่สามารถแยกออกจากระบบที่นำมาใช้

รูปแบบโครงสร้าง: สิ่งนี้ทำให้การออกแบบง่ายขึ้นโดยระบุวิธีง่ายๆ ในการตระหนักถึงความสัมพันธ์ระหว่างเอนทิตี

รูปแบบพฤติกรรม: ใช้ในการจัดการความสัมพันธ์ ความรับผิดชอบ และอัลกอริธึมระหว่างอ็อบเจกต์

แบบโรงงาน

ใช้รูปแบบโรงงานเพื่อสร้างวัตถุ ถูกต้อง — สร้างวัตถุและไม่สร้างวัตถุ เมื่อเราสร้างวัตถุ เราจะสร้างมันขึ้นมาก่อนแล้วจึงเริ่มต้นมัน โดยปกติ ต้องใช้ตรรกะบางอย่างและดำเนินการหลายขั้นตอน ด้วยเหตุนี้ การมีทุกสิ่งไว้ในที่เดียวและนำกลับมาใช้ใหม่ได้ทุกเมื่อที่คุณต้องการสร้างออบเจ็กต์ใหม่ในลักษณะเดียวกันจึงเป็นเรื่องที่สมเหตุสมผล โดยพื้นฐานแล้วนั่นคือการใช้รูปแบบโรงงาน
เป็นความคิดที่ดีที่จะมีอินเทอร์เฟซสำหรับโรงงานของเราและให้โค้ดของเราพึ่งพามัน ไม่ใช่ในโรงงานที่เป็นรูปธรรม

 interface FamilyFactoryInterface { public function create() : Family }

ถัดไป ใช้ส่วนต่อประสานโรงงานกับคลาสต่อไปนี้:

 class FamilyFactory implements FamilyFactoryInterface { public function create() : Family { $family = new Family(); // initialize your family return $family; } }

รูปแบบอะแดปเตอร์

ใน Adapter Design Pattern คลาสจะเปลี่ยนอินเตอร์เฟสของคลาสหนึ่งเป็นคลาสอื่น ในตัวอย่างนี้ เรามีคลาส TextBook ที่มีเมธอด getTitle() และ getAuthor() ลูกค้าคาดหวังเมธอด getTitleAndAuthor() เพื่อ "ปรับ" SimpleBook สำหรับ demoAdapter เรามีคลาสอแด็ปเตอร์ BookAdapter ซึ่งรับอินสแตนซ์ของ TextBook และใช้ TextBook getTitle() และเมธอด getAuthor() ในเมธอด getTitleAndAuthor ของตัวเอง

 <?php class TextBook { private $title; private $author; function __construct($title_in, $author_in) { $this->title = $title_in; $this->author = $author_in; } function getTitle() { return $this->title; } function getAuthor() { return $this->author; } } class BookAdapter { private $book; function __construct(TextBook $book_in) { $this->book = $book_in; } function getTitleAndAuthors() { return $this->book->getTitle().' by '.$this->book->getAuthor(); } } // client writeln('BEGIN TESTING ADAPTER PATTERN'); writeln(''); $book = new TextBook("Gamma, Helm, Johnson, and Vlissides", "Design Patterns"); $bookAdapter = new BookAdapter($book); writeln('Author and Title: '.$bookAdapter->getTitleAndAuthor()); writeln(''); writeln('END TESTING ADAPTER PATTERN'); function writeln($line_in) { echo $line_in."<br/>"; } ?>

รูปแบบ PHP Singleton

เพื่อจำกัดการสร้างอินสแตนซ์ของคลาสให้เป็นอ็อบเจ็กต์เดียว จะใช้รูปแบบซิงเกิลตันใน PHP สิ่งนี้มีประโยชน์เมื่อต้องการเพียงหนึ่งอ็อบเจ็กต์ทั่วทั้งระบบ เหมาะสมที่จะอนุญาตให้เข้าถึงอินสแตนซ์ของบางคลาสได้เพียงอินสแตนซ์เดียวในขณะออกแบบเว็บแอปพลิเคชัน เพื่อป้องกันการสร้างอ็อบเจ็กต์อย่างชัดเจนจากคลาสรูปแบบซิงเกิลตัน คอนสตรัคเตอร์ส่วนตัวจะถูกใช้

 <?php class Singleton { public static function getInstance() { static $instance = null; if (null === $instance) { $instance = new static(); } return $instance; } protected function __construct() { } private function __clone() { } private function __wakeup() { } } class SingletonChild extends Singleton { } $obj = Singleton::getInstance(); var_dump($obj === Singleton::getInstance()); $obj2 = SingletonChild::getInstance(); var_dump($obj2 === Singleton::getInstance()); var_dump($obj2 === SingletonChild::getInstance()); ?>

รูปแบบผู้สังเกตใน PHP

รูปแบบ PHP Observer ใช้เพื่อเตือนส่วนที่เหลือของระบบเกี่ยวกับเหตุการณ์เฉพาะในบางสถานที่
เช่น หากเราจำเป็นต้องสร้าง โรงละคร เพื่อแสดงภาพยนตร์ต่อนักวิจารณ์ เรากำหนดคลาส โรงละคร ด้วยวิธีการปัจจุบัน ก่อนนำเสนอหนัง เราต้องการส่งข้อความไปยังโทรศัพท์มือถือของนักวิจารณ์ จากนั้นในช่วงกลางของภาพยนตร์เราต้องการหยุดภาพยนตร์เป็นเวลา 5 นาทีเพื่อให้นักวิจารณ์มีช่วงเวลา สุดท้าย หลังจากหนังจบ เราต้องการขอให้นักวิจารณ์ทิ้งคำตอบไว้ ดังนั้นในรูปแบบผู้สังเกตการณ์สำหรับ PHP ออบเจ็กต์ผู้สังเกตการณ์จะได้รับการแจ้งเตือนเมื่อสถานะเปลี่ยนเท่านั้น

นี่คือลักษณะของรหัส -

 class Theater { public function current(Movie $movie) : void { $critics = $movie->getCritics(); $this->message->send($critics, '...'); $movie->play(); $movie->pause(5); $this->progress->interval($critics) $movie->end(); $this->response->request($critics); } }

รูปแบบมัณฑนากรสำหรับ PHP

รูปแบบมัณฑนากรจะใช้เมื่อคุณต้องการเปลี่ยนลักษณะของวัตถุในขณะใช้งาน และลดจำนวนการสืบทอดที่ไม่จำเป็นและจำนวนคลาส มันสามารถอธิบายได้ด้วยตัวอย่าง สมมติว่าเรามีคลาส Sofa and Bed และทั้งคู่ใช้ SleeperInterface

 interface SleeprInterface { public function sleep() : void; } class Sofa implements SleeperInterface { public function sleep() : void { // sleeps on sofa } } class Bed implements SleeperInterface { public function sleep() : void { // sleeps on bed } }

ทั้งโซฟาและเตียงนอนมีพฤติกรรมการนอนหลับเหมือนกัน ตอนนี้ เราต้องการโซฟาและเตียงอื่นๆ ที่มีฟังก์ชันการทำงานเพิ่มเติมที่จะบอกให้ผู้ใช้ทราบถึงการติดตามการนอนหลับเมื่อนอนบนโซฟาหรือเตียง ด้วยการสืบทอด เราสามารถแก้ปัญหานี้ได้ดังนี้:

 class SmartSofa extends Sofa { public function sleep() : void { parent::sleep(); $this->sleepHours(); } } class SmartBed extends Window { public function sleep() : void { parent::sleep(); $this->sleepHours(); } }


ตอนนี้เรามีทั้งหมด 4 คลาส อย่างไรก็ตาม เราสามารถแก้ปัญหานี้ได้ 3 คลาสด้วยลวดลายมัณฑนากรเท่านั้น นี่คือวิธี:

 class SmartSleeper implements SleeperInterface { private $sleeper; public function __construct(SleeperInterface $sleeper) { $this->sleeper = $sleeper; } public function sleep() : void { $this->sleeper->sleep(); $this->sleepHours(); } } $sofa = new Sofa(); $bed = new Bed(); $smartSofa = new SmartSleeper($sofa); $smartBed = new SmartSleeper($bed);

ที่นี่ เราได้เปิดตัวสลีปรูปแบบใหม่ที่ทำหน้าที่เหมือนพร็อกซี แต่มีฟังก์ชันพิเศษอยู่ด้านบน

ใช้ประโยชน์จากรูปแบบการออกแบบใน Drupal 9

แม้ว่าจะมีรูปแบบการออกแบบมากมายที่สร้างไว้แล้วใน Drupal ก่อน Drupal 9 แต่ตอนนี้มีรูปแบบอื่นๆ อีกมากมายที่ก่อนหน้านี้ไม่พร้อมใช้งาน รูปแบบใหม่เหล่านี้บางส่วนมาแทนที่รูปแบบเก่าอย่างสมบูรณ์ ในขณะที่รูปแบบอื่นๆ ได้แนะนำคุณสมบัติใหม่บางอย่างให้กับ Drupal 9
รูปแบบการออกแบบที่ใช้ใน Drupal 9 ได้แก่ :

  • รูปแบบการเขียนโปรแกรมเชิงวัตถุ (OOP)
  • การฉีดพึ่งพา
  • แบบโรงงาน
  • รูปแบบซิงเกิล

OOP ไม่ใช่รูปแบบเดียวจริงๆ แต่เป็นวิธีคิดและจัดโครงสร้างโค้ดที่ต่างไปจากเดิมอย่างสิ้นเชิง ซึ่งทำได้มากกว่าแค่รูปแบบการออกแบบ เป็นพื้นฐานสำหรับรูปแบบการออกแบบซอฟต์แวร์ยอดนิยมจำนวนมากที่ใช้อยู่ในปัจจุบัน รวมถึงรูปแบบที่ใช้ใน Drupal 9 ซึ่งเปิดตัวใน Drupal 7 แต่ไม่ได้ใช้อย่างกว้างขวาง และไม่จำเป็น สถานการณ์ใน Drupal 9 เปลี่ยนไปแล้ว มีการใช้กันอย่างแพร่หลายและมีความจำเป็น

การฉีดพึ่งพา

การฉีดการพึ่งพาเป็นรูปแบบการออกแบบซอฟต์แวร์ที่จะช่วยให้คุณลบการพึ่งพาแบบฮาร์ดโค้ดและทำให้สามารถเปลี่ยนแปลงได้บนรันไทม์หรือในเวลาคอมไพล์ การเพิ่มการพึ่งพาการฉีดเป็นเรื่องง่ายและไม่ยุ่งกับรหัสที่มีอยู่ของคุณ Drupal 8 นำเสนอแนวคิดของบริการเพื่อแยกฟังก์ชันการทำงานที่ใช้ซ้ำได้ core.services.yml เป็นตัวอย่างของการพึ่งพาการฉีดใน Drupal 9 เราได้พูดถึง Factory Pattern และ Singleton Pattern ใน PHP ก่อนหน้านี้แล้ว

ปัจจุบันใน Drupal การพึ่งพาการฉีดเป็นวิธีที่แนะนำสำหรับการเข้าถึงและใช้บริการ และควรใช้เมื่อทำได้ แทนที่จะเรียกใช้คอนเทนเนอร์บริการส่วนกลาง บริการจะถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังคอนสตรัคเตอร์หรือฉีดผ่านเมธอด setter การส่งผ่านบริการที่วัตถุขึ้นอยู่กับอย่างชัดเจนเรียกว่าการพึ่งพาการฉีด ในหลายกรณี การพึ่งพาถูกส่งผ่านอย่างชัดเจนในตัวสร้างคลาส

ตรวจสอบหน้านี้เพื่อค้นหาบริการที่มีทั้งหมดใน Drupal core คุณสามารถอ่านเพิ่มเติมเกี่ยวกับบริการได้ในเอกสารประกอบของ Drupal

ให้เราพิจารณาบริการ 'entity_type.manager' เป็นตัวอย่างเพื่อรับชื่อโหนดที่มี ID=1 เพื่อที่จะฉีดเข้าไปในบริการที่กำหนดเองของเรา เราแค่ต้องใช้ชื่อบริการและส่งผ่านเป็นอาร์กิวเมนต์ในไฟล์ my_module_name.services.yml ดังที่แสดงด้านล่าง:

my_module_name.services.yml

 services: my_module_name.helper: class: Drupal\my_module_name\MyModuleHelper arguments: ['@entity_type.manager']

จากนั้นในคลาสบริการของเรา เราแค่ต้องรับบริการในเมธอด _ _construct และเก็บไว้ในตัวแปรดังนี้:

MyModuleHelper.php

 <?php namespace Drupal\my_module_name; use Drupal\Core\Entity\EntityTypeManagerInterface; /** * MyModuleHelper is a simple example of a Drupal 9 service. */ class MyModuleHelper { /** * The entity type manager. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; /** * Part of the DependencyInjection magic happening here. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. */ public function __construct(EntityTypeManagerInterface $entity_type_manager) { $this->entityTypeManager = $entity_type_manager; } /** * Returns a title for node_id = 1. */ public function getFirstNodeTitle() { $node = $this->entityTypeManager->getStorage('node')->load(1); return $node->getTitle(); } }

จากนั้นเราสามารถใช้บริการตัวจัดการประเภทเอนทิตีและรับชื่อของโหนดด้วย nid=1 ในเมธอด getFirstNodeTitle

ขอบคุณมากสำหรับ Ankitha Shetty สำหรับข้อมูลเชิงลึกของเธอที่ช่วยเราอัปเดตบทความ