أضف سلوكًا باستخدام الأساليب

مكتمل

يُعد الهدف النهائي للنظام هو إنتاج مخرجات مفيدة. للوصول إلى ذلك، عليك معالجة الإدخال. أثناء المعالجة، قد تحتاج إلى مساعدة من أساليب وبيانات متنوعة. في البرمجة الموجهة للعناصر (OOP)، تُوضع الأساليب والبيانات على العناصر. لمعالجة الإدخال وإنتاج نتيجة في برمجة موجة للعناصر، تحتاج إلى الأساليب.

الأساليب في OOP

بغض النظر عن النموذج المُستخدم، يمكن للأساليب أن تنفذ الإجراء. قد يكون هذا الإجراء حساب يعتمد فقط على الإدخالات، أو قد يغير قيمة متغير.

تأتي الأساليب على العناصر في برمجة موجة للعناصر في نوعين:

  • الأساليب الخارجية، يمكن للعناصر الأخرى استدعاؤها.
  • الأساليب الداخلية، لا يمكن للعناصر الأخرى الوصول إليها. بالإضافة إلى ذلك، تساعد الطرق الداخلية في تنفيذ مهمة بدأت باستدعاء طريقة خارجية.

بغض النظر عن نوع الأسلوب، يمكنهم تغيير قيمة سمة العنصر، وبمعنى آخر، حالته.

إن فكرة الحالة ومن وماذا يمكن أن يغيرها، يُعد موضوعًا مهمًا. كما أنه جزء مهم من تصميم الفئات والعنصر الخاص بك. تلك هي الأسئلة تقودنا إلى القسم التالي، التغليف.

التغليف: حماية بياناتك

الفكرة العامة للتغليف هي أن البيانات الموجودة على عنصر تكون داخلية، وهو أمر يتعلق بالعنصر فقط. تُعد البيانات ضرورية من أجل العنصر والأساليب لتنفيذ ذلك، بمعنى تنفيذ المهمة. عندما تقول أن البيانات داخلية، فأنت تقول إنه يجب حمايتها من التلاعب الخارجي الآخر، أو بالأحرى، من التلاعب الخارجي غير المتحكم فيه. السؤال هو، لماذا؟

لماذا تحتاج إليها

لنشرح أسباب عدم ضرورة لمس البيانات مباشرةً من خلال أي عنصر آخر. وإليك بعض الأمثلة:

  • لا تحتاج إلى معرفة الأساليب الداخلية. عندما تقود السيارة، فأنت تضغط على دواسة للتحكم في القابض أو للتسريع أو الفرامل. لأنك تشغل التعليمات البرمجية على مستوى أعلى، فأنت لا تهتم بما يجري داخلها، وكيف تنفذ التعليمات البرمجية العمل. يحدث الشيء ذاته مع التعليمة البرمجية. لا تحتاج دائمًا إلى معرفة كيفية عمل شيء ما، طالما أن هناك طريقة يُمكنك استدعاؤها لفعل ما تريد.

  • لا ينبغي أن تعرف الوسائل الداخلية. بدلاً من وجود دواسة للتفاعل مع السيارة، تخيل أنه لديك مفك أو عدة لحام في محاولة التسريع. يبدو شيئًا مخيفًا، صحيح؟ هذا لأن الوضع كذلك. أو لنفترض أن لديك مثالاً أكثر واقعية، فئة مربعة، مع التعليمات البرمجية التالية:

    class Square:
         def __init__(self):
             self.height = 2
             self.width = 2
         def set_side(self, new_side):
             self.height = new_side
             self.width = new_side
    
    square = Square()
    square.height = 3 # not a square anymore
    

    في المثال المربع، يمكنك تحليل فكرة ماهية المربع من خلال تعيين height المتغير. الطريقة التي يتم بها ترميز المربع، تطلب منك استدعاء الأسلوب set_side() للمربع لكي تعمل بشكل صحيح. السماح للعنصر بالاهتمام بالبيانات الخاصة به يعتبر أكثر أمانًا. في كل حالة تقريبًا، ينبغي لك اختيار التفاعل عبر طريقة في مقابل إعداد البيانات بشكل صريح.

مستويات الوصول

كيف يمكنك حماية الفئة والعنصر الخاص بك من التلاعب غير المرغوب فيه الخاص بالبيانات؟ الإجابة هي باستخدام مستويات الوصول. يمكنك إخفاء البيانات عن العالم الخارجي، من العناصر الأخرى وذلك بوضع علامة على البيانات والوظائف باستخدام كلمات رئيسية محددة. تُعرف تلك الكلمات الأساسية باسم معدلات الوصول.

الطريقة التي يحقق بها بيثون إخفاء البيانات هي من خلال إضافة البادئات إلى أسماء السمات. تضم أحد تلك التسطيرات السفلية البادئة _ رسالة إلى العالم الخارجي مفادها أنه لا ينبغي المساس بتلك البيانات. عند تعديل فئة المربع ينتهي بك الأمر مع تلك التعليمات البرمجية:

  class Square:
      def __init__(self):
          self._height = 2
          self._width = 2
      def set_side(self, new_side):
          self._height = new_side
          self._width = new_side

  square = Square()
  square._height = 3 # not a square anymore

لا يزال أحد التسطيرات السفلية البادئة يتيح تعديل البيانات، وتشير بيثون إلى ذلك بأنه محمي. هل يمكننا فعل ذلك بشكل أفضل؟ نعم يمكننا، من خلال وجود نوعين من التسطيرات السفلية البادئة، __ والتي يُشار إليها على أنها خاصة. يجب أن تبدو الفئة المربعة الآن مثل هذه التعليمة البرمجية:

  class Square:
      def __init__(self):
          self.__height = 2
          self.__width = 2
    def set_side(self, new_side):
          self.__height = new_side
          self.__width = new_side

  square = Square()
  square.__height = 3 # raises AttributeError

رائع، نحن في أمان الآن. هل وفرنا الحماية لبياناتنا؟ حسنًا، ليس كليًا. يغير بيثون اسم البيانات الأساسية فقط. بمجرد إدخال هذه التعليمة البرمجية، لا يزال بإمكانك تغيير قيمتها:

square = Square()
square._Square__height = 3 # is allowed

تحل العديد من اللغات الأخرى التي تطبق حماية البيانات هذه المشكلة بشكل مختلف. تعد بيثون فريدة من نوعها في أن حماية البيانات تشبه مستويات الاقتراح بدلاً من تنفيذها بدقة.

ما هي الجهات الممنوحة والمستحوذة؟

لقد قلنا حتى الآن إن البيانات بشكل عام لا ينبغي المساس بها من الخارج. تتعلق البيانات بالعنصر. كما هو الحال في جميع القواعد والتوصيات القوية، توجد استثناءات. تحتاج أحياناً إلى تغيير البيانات، أو يكون تغييرها أسهل من الاضطرار إلى إضافة قدر كبير من التعليمات البرمجية.

الجهات الممنوحة والمستحوذة، والتي تُعرف أيضًا باسم الإكسسوارات والقوى،هي أساليب مخصصة لقراءة بياناتك أو تغيرها. تلعب الجهات الممنوحة دورًا في جعل البيانات الداخلية الخاصة بك قابلة للقراءة في الخارج، والتي لا تبدو سيئة للغاية، أليس كذلك؟ تعد الجهات المستحوذة هي الأساليب التي تغيير بياناتك مباشرةً. الفكرة هي الجهات المستحوذة هي التصرف كحارس بحيث لا يمكن تعيين قيمة سيئة. لنطرح فئة مربع مرة أخرى ونر المحصلات والمعينات في الإجراء:

  class Square:
      def __init__(self):
          self.__height = 2
          self.__width = 2
      def set_side(self, new_side):
          self.__height = new_side
          self.__width = new_side
      def get_height(self):
          return self.__height
      def set_height(self, h):
          if h >= 0:
              self.__height = h
          else:
              raise Exception("value needs to be 0 or larger")

  square = Square()
  square.__height = 3 # raises AttributeError

الأسلوب set_height() يحميك من تعيين القيمة إلى شيء سلبي. إذا فعلت ذلك، فإنه يثير الاستثناء.

استخدام مصمم الديكور للحصول على معينات

مصممو الديكور موضوع مهم في بيثون. كمّا أنه جزء من موضوع أكبر يسمى البرمجة الفوقية. مصممو الديكور من الوظائف التي تأخذ وظيفتك كمدخل. تتمثل الفكرة في ترميز وظائف قابلة لإعادة استخدام على أنها وظائف مصممي الديكور ومن ثم تزيين وظائف أخرى معها. يُغد الغرض من ذلك هو إعطاء وظيفة ميزة لم تكن موجودة بها من قبل. يمكن لمصمم الديكور، مثلاً، إضافة حقول إلى الكائن الخاص بك، وقياس الوقت الذي يستغرقه لاستدعاء وظيفة، والقيام بأكثر من ذلك بكثير.

في سياق OOP والمحصلات والمعينات، يمكن أن يساعدك مصمم ديكور معين @property على إزالة بعض التعليمات البرمجية المتداول عند إضافة المحصلات والمعينات. يتولى @propertyمصمم الديكور الأمور التالية:

  • إنشاء حقل دعم: عند تزيين وظيفة من خلال @property مصمم الديكور، فإنه يخلق حقل دعم خاصًا. يمكنك تجاوز هذا السلوك إذا أردت، ولكن يُستحسن أن يكون لديك سلوك افتراضي.
  • تحديد المعين: يمكن لأسلوب المعين تغيير الحقل النسخ.
  • تحديد المحصلة: ينبغي أن تراجع الدالة حقل النسخ.
  • تحديد وظيفة الحذف: يمكن لهذه الوظيفة أن تحذف الحقل.

لنر مصمم الديكور هذا في العمل:

class Square:
    def __init__(self, w, h):
        self.__height = h
        self.__width = w
  
    def set_side(self, new_side):
        self.__height = new_side
        self.__width = new_side

    @property
    def height(self):
        return self.__height

    @height.setter
    def height(self, new_value):
        if new_value >= 0:
            self.__height = new_value
        else:
            raise Exception("Value must be larger than 0")

في التعليمات البرمجية السابقة، تم تزيين الوظيفة height() من خلال مصمم الديكور @property. إجراء الديكور هذا يخلق المجال الخاص__height. __heightلم يتم تعريف الحقل في الدالة الإنشائية __init__() لأن مصمم الديكور يفعل ذلك بالفعل. يوجد أيضًا ديكور آخر وهو @height.setter. يشير هذا الديكور إلى طريقة مشابهة في المظهر height() لأسلوب المعين. تأخذ طريقة الارتفاع الجديدة معلمة أخرى value كمعامل ثاني لها.

القدرة على معالجة الارتفاع بعيدًا عن العرض لا تزال تسبب مشكلة. ستحتاج إلى فهم ما يفعله الفصل قبل التفكير في السماح للمحصلات والمعينات، لأنك تقوم بإدخال المخاطر.