รูปแบบการออกแบบอแด็ปเตอร์ใน Python

บทนำ

พื้นที่ รูปแบบการออกแบบอแดปเตอร์ เป็นที่นิยม รูปแบบการออกแบบโครงสร้าง ใช้ในงานวิศวกรรมซอฟต์แวร์ คู่มือนี้จะกล่าวถึงวิธีที่เราสามารถนำรูปแบบการออกแบบอแด็ปเตอร์ไปใช้ใน Python

รูปแบบการออกแบบ เป็นโซลูชันที่เหมือนเทมเพลต - เป็นสูตรที่ใช้งานได้จริงสำหรับการแก้ปัญหาที่เกิดซ้ำและทั่วไปในการพัฒนาซอฟต์แวร์ รูปแบบของอแดปเตอร์ขึ้นอยู่กับแนวคิดของอแดปเตอร์ในโลกแห่งความเป็นจริง! ตัวอย่างเช่น ที่ชาร์จของแล็ปท็อปอาจมีปลั๊ก 3 ขาที่ปลาย แต่เต้ารับบนผนังอาจเป็นเต้ารับ 2 ขาเท่านั้น ในการเสียบที่ชาร์จแบบ 3 พินเข้ากับเต้ารับนี้ เราจำเป็นต้องมีอะแดปเตอร์ที่ยอมรับปลั๊กแบบ 3 พิน และ ปรับ อินเทอร์เฟซเข้าสู่ 2 ขา เบ้า.

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

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

มาดูคำศัพท์พื้นฐานกันก่อนดำดิ่งสู่โลกของรูปแบบอแด็ปเตอร์:

  • อินเทอร์เฟซไคลเอ็นต์: อินเทอร์เฟซที่ระบุฟังก์ชันที่ไคลเอ็นต์ควรนำไปใช้
  • ไคลเอนต์: คลาสที่ใช้ส่วนต่อประสานไคลเอ็นต์
  • อะแดปเตอร์/บริการ: คลาสที่เข้ากันไม่ได้ซึ่งต้องทำงานร่วมกับอินเทอร์เฟซไคลเอ็นต์
  • อะแดปเตอร์: คลาสที่ทำให้การทำงานร่วมกันระหว่างบริการและลูกค้าเป็นไปได้

รูปแบบอะแดปเตอร์ประเภทต่างๆ

รูปแบบการออกแบบอแด็ปเตอร์สามารถใช้งานได้สองวิธี:

อะแดปเตอร์วัตถุ

ด้วยเมธอดนี้ คลาสอะแด็ปเตอร์ใช้เมธอดจากอินเทอร์เฟซไคลเอ็นต์ ดังนั้น อ็อบเจ็กต์ไคลเอ็นต์และอ็อบเจ็กต์อะแด็ปเตอร์จึงเข้ากันได้ ออบเจ็กต์บริการสร้าง a has-a ความสัมพันธ์กับอ็อบเจ็กต์อะแด็ปเตอร์ เช่น อ็อบเจ็กต์บริการเป็นของอ็อบเจ็กต์อะแด็ปเตอร์

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

เราสามารถใช้อ็อบเจ็กต์อะแด็ปเตอร์ในภาษาการเขียนโปรแกรมสมัยใหม่ทั้งหมดได้

คลาสอะแด็ปเตอร์

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

การนำรูปแบบการออกแบบอแด็ปเตอร์ไปใช้ใน Python

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

ปัญหาความไม่ลงรอยกันระหว่างชั้นเรียน

มาดูปัญหาความเข้ากันได้กันเมื่อไคลเอนต์และคลาสบริการใช้ฟังก์ชันการทำงานที่แตกต่างกัน สร้างคลาสไคลเอนต์ด้วยวิธีการต่อไปนี้และบันทึกไว้ในโฟลเดอร์เป็น car.py:

import random

class Car:
    def __init__(self):
        self.generator = random.Random()

    def accelerate(self):
        random_num = self.generator.randint(50, 100)
        speed = random_num
        print(f"The speed of the car is {speed} mph")

    def apply_brakes(self):
        random_num = self.generator.randint(20, 40)
        speed = random_num
        print(f"The speed of the car is {speed} mph after applying the brakes")

    def assign_driver(self, driver_name):
        print(f"{driver_name} is driving the car")

ที่นี่เราได้สร้าง Car คลาสที่มีสามวิธี accelerate(), apply_brakes() และ assign_driver(). เรานำเข้า random และใช้เพื่อสร้างตัวเลขที่กำหนดความเร็วของรถหลังจากเร่งและเหยียบเบรก ดิ assign_driver() วิธีแสดงชื่อผู้ขับขี่รถยนต์

ต่อไปเราต้องสร้าง service หรือ adaptee class ที่ต้องการจะทำงานร่วมกับ client class Car. สร้างคลาส Motorcycle แบบนี้และบันทึกไว้ในโฟลเดอร์ของคุณเป็น motorcycle.py:

import random

class Motorcycle:
    def __init__(self):
        self.generator = random.Random()

    def rev_throttle(self):
        random_num = self.generator.randint(50, 100)
        speed = random_num
        print(f"The speed of the motorcycle is {speed} mph")

    def pull_brake_lever(self):
        random_num = self.generator.randint(20, 40)
        speed = random_num
        print(
            f"The speed of the motorcycle is {speed} mph after applying the brakes")

    def assign_rider(self, rider_name):
        print(f"{rider_name} is riding the motorcycle")  

คลาสบริการ, Motorcycle ถูกสร้างขึ้นด้านบนด้วยสามวิธี rev_throttle(), pull_brake_lever()และ assign_rider(). สังเกตความแตกต่างระหว่างวิธีการของบริการและคลาสไคลเอ็นต์ แม้ว่าจะมีฟังก์ชันที่คล้ายคลึงกัน ดิ accelerator() วิธีเพิ่มความเร็วของรถในขณะที่ rev_throttle() วิธีเพิ่มความเร็วของรถจักรยานยนต์ เช่นเดียวกัน, apply_brakes() และ pull_brake_lever() ใช้เบรกในรถยนต์แต่ละคัน ในที่สุด assign_driver() และ assign_rider() วิธีการกำหนดผู้ควบคุมรถ

ต่อไป มาสร้างคลาสเพื่อเข้าถึงวิธีการต่างๆ เหล่านี้กัน ขั้นแรกให้เพิ่ม an __init.py__ ในโฟลเดอร์เดียวกับที่คุณสร้าง car.py และ motorcycle.py:

touch __init__.py

ตอนนี้เพิ่มรหัสต่อไปนี้ในไฟล์ใหม่ drive.py:

from car import Car
from motorcycle import Motorcycle
import traceback

if __name__ == '__main__':
    car = Car()
    bike = Motorcycle()

    print("The Motorcyclen")
    bike.assign_rider("Subodh")
    bike.rev_throttle()
    bike.pull_brake_lever()
    print("n")

    print("The Carn")
    car.assign_driver("Sushant")
    car.accelerate()
    car.apply_brakes()
    print("n")

    print("Attempting to call client methods with the service objectn")

    try:
        bike.assign_driver("Robert")
        bike.accelerate()
        bike.apply_brakes()
    except AttributeError:
        print("Oops! bike object cannot access car methods")
        traceback.print_exc()

สคริปต์นี้ที่สร้างลูกค้าและวัตถุบริการของเรา ก่อนอื่นเรานำเข้า Car และ Motorcycle คลาสและสร้างวัตถุกับพวกเขา จากนั้นเราเรียกใช้วิธีการจาก bike วัตถุ (Motorcycle ระดับ). หลังจากนั้นเราเรียกใช้วิธีการของ car วัตถุ (Car ระดับ). เมื่อดำเนินการแล้ว โค้ดทั้งหมดที่กล่าวถึงจนถึงตอนนี้จะใช้งานได้

อย่างไรก็ตาม มีข้อยกเว้นเกิดขึ้นเมื่อเราพยายามเรียกใช้เมธอดของ Car คลาสกับ bike วัตถุ. เมื่อเราเรียกใช้สคริปต์นี้:

The Motorcycle

Subodh is riding the motorcycle
The speed of the motorcycle is 91 mph
The speed of the motorcycle is 37 mph after applying the brakes


The Car

Sushant is driving the car
The speed of the car is 59 mph
The speed of the car is 33 mph after applying the brakes


Attempting to call client methods with the service object

Oops! bike object cannot access car methods
Traceback (most recent call last):
  File "drive.py", line 24, in 
    bike.assign_driver("Robert")
AttributeError: 'Motorcycle' object has no attribute 'assign_driver'

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

แต่เราสามารถใช้อะแดปเตอร์เพื่อเชื่อมช่องว่างความเข้ากันได้ระหว่างรหัสลูกค้าและวัตถุบริการของเรา

การใช้อแดปเตอร์เพื่อแก้ไขปัญหาความเข้ากันไม่ได้

ในไฟล์ใหม่ motorcycle_adapter.pyให้เพิ่มคลาสต่อไปนี้:

class MotorcycleAdapter:

    def __init__(self, motorcycle):
        self.motorcycle = motorcycle

    def accelerate(self):
        self.motorcycle.rev_throttle()

    def apply_brakes(self):
        self.motorcycle.pull_brake_lever()

    def assign_driver(self, name):
        self.motorcycle.assign_rider(name)

เราสร้าง MotorcycleAdapter คลาสซึ่งสร้างอินสแตนซ์ตัวเองด้วยวัตถุบริการ (motorcycle). อะแด็ปเตอร์ใช้เมธอดของไคลเอ็นต์ซึ่งได้แก่ accelerate(), apply_brakes() และ assign_driver(). ภายในร่างกายของ accelerate() วิธีการ เราได้ใช้ motorcycle อินสแตนซ์ของวัตถุบริการที่จะเรียก rev_throttle() วิธีการบริการ ในทำนองเดียวกัน วิธีอื่นใช้วิธีการที่สอดคล้องกันของ Motorcycle ชั้นเรียน

มาอัพเดทกันจ้า drive.py เพื่อให้เราสามารถใช้อแดปเตอร์ในการ try/except บล็อก:

from car import Car
from motorcycle import Motorcycle
from motorcycle_adapter import MotorcycleAdapter 
import traceback

if __name__ == '__main__':
    car = Car()
    bike = Motorcycle()
    bike_adapter = MotorcycleAdapter(bike) 

    ...

    try:
        print("Attempting to call client methods with the service object using an adaptern")
        bike_adapter.assign_driver("Robert")
        bike_adapter.accelerate()
        bike_adapter.apply_brakes()
    except AttributeError:
        print("Oops! bike object cannot access car methods")
        traceback.print_exc()

ที่นี่bike_adapter เป็นวัตถุของ MotorcycleAdapter ระดับ. เราจัดหา bike คัดค้าน MotorcycleAdapter ตัวสร้างคลาส การดำเนินการสคริปต์นี้ให้ผลลัพธ์ต่อไปนี้แก่เรา:

ดูคู่มือเชิงปฏิบัติสำหรับการเรียนรู้ Git ที่มีแนวทางปฏิบัติที่ดีที่สุด มาตรฐานที่ยอมรับในอุตสาหกรรม และเอกสารสรุปรวม หยุดคำสั่ง Googling Git และจริงๆ แล้ว เรียน มัน!

The Motorcycle

Subodh is riding the motorcycle
The speed of the motorcycle is 88 mph
The speed of the motorcycle is 35 mph after applying the brakes


The Car

Sushant is driving the car
The speed of the car is 91 mph
The speed of the car is 24 mph after applying the brakes


Attempting to call client methods with the service object

Attempting to call client methods with the service object using an adapter

Robert is riding the motorcyle
The speed of the motorcycle is 67 mph
The speed of the motorcycle is 25 mph after applying the brakes

โดยไม่ต้องปรับพื้นฐาน Motorcycle คลาส เราก็ทำให้ใช้งานได้เหมือน Car ใช้อแดปเตอร์!

ข้อดีและข้อเสียของรูปแบบการออกแบบอแด็ปเตอร์

ข้อดีของรูปแบบอะแดปเตอร์คือ:

  • เราสามารถบรรลุ coupling ต่ำระหว่างคลาสอะแด็ปเตอร์และคลาสไคลเอนต์
  • เราสามารถนำคลาสอะแด็ปเตอร์กลับมาใช้ใหม่เพื่อรวมคลาสบริการจำนวนมากในแอปพลิเคชัน
  • เราสามารถเพิ่มความยืดหยุ่นของโปรแกรมได้โดยการแนะนำอะแดปเตอร์หลายตัวโดยไม่รบกวนรหัสไคลเอนต์

ข้อเสียของรูปแบบอะแดปเตอร์คือ:

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

สรุป

ในบทความนี้ เราได้เรียนรู้เกี่ยวกับรูปแบบการออกแบบอะแดปเตอร์ ประเภทของอะแดปเตอร์ และปัญหาที่พวกเขาแก้ไข เราใช้รูปแบบอแด็ปเตอร์ใน Python เพื่อให้เราสามารถโต้ตอบกับ a Motorcycle วัตถุ เช่น a Car วัตถุโดยใช้อะแดปเตอร์เพื่อให้อินเทอร์เฟซของแต่ละคลาสไม่เปลี่ยนแปลง

ประทับเวลา:

เพิ่มเติมจาก สแต็ค