التغليف الداخلي للحقول (Self Encapsulate Fields)

من موسوعة حسوب

ملاحظة قبل البدء: تختلف هذه التقنية عن تقنية تغليف الحقول (Encapsulate Field) من حيث أنّها تُستخدَم لتغليف الحقول الخاصّة (أي المُحدَّدة بالكلمة المفتاحيّة private).

المشكلة

الوصول المباشر إلى الحقول الخاصّة (private fields) داخل الصنف (class).

الحل

إنشاء تابعي الوصول getter و setter للحقل الخاصّ ومنع الوصول إليه إلا عبرهما.

مثال

قبل إعادة التصميم

يحتوي الصنف Range على الحقلين low و high من النوع الخاص (private) ونلاحظ الوصول إليهما مباشرةً داخل التابع includes في الشيفرة الآتية:

في لغة Java:

class Range {
  private int low, high;
  boolean includes(int arg) {
    return arg >= low && arg <= high;
  }
}

في لغة C#‎:

class Range 
{
  private int low, high;
  
  bool Includes(int arg) 
  {
    return arg >= low && arg <= high;
  }
}

في لغة PHP:

private $low;
private $high;

function includes($arg) {
  return $arg >= $this->low && $arg <= $this->high;
}

في لغة TypeScript:

class Range {
  private low: number
  private high: number;
  includes(arg: number): boolean {
    return arg >= low && arg <= high;
  }
}

بعد إعادة التصميم

أُضيف تابع الوصول getter للحقلين low و high بهدف إجراء التغليف الداخليّ لهما، وأصبح الوصول لهما في التابع includes عبر تابعي الوصول المُنشَأين، لتصبح الشيفرة كما يلي:

في لغة Java:

class Range {
  private int low, high;
  boolean includes(int arg) {
    return arg >= getLow() && arg <= getHigh();
  }
  int getLow() {
    return low;
  }
  int getHigh() {
    return high;
  }
}

في لغة C#‎:

class Range 
{
  private int low, high;
  
  int Low {
    get { return low; }
  }
  int High {
    get { return high; }
  }
  
  bool Includes(int arg) 
  {
    return arg >= Low && arg <= High;
  }
}

في لغة PHP:

private $low;
private $high;

function includes($arg) {
  return $arg >= $this->getLow() && $arg <= $this->getHigh();
}
function getLow() {
  return $this->low;
}
function getHigh() {
  return $this->high;
}

في لغة TypeScript:

class Range {
  private low: number
  private high: number;
  includes(arg: number): boolean {
    return arg >= getLow() && arg <= getHigh();
  }
  getLow(): number {
    return low;
  }
  getHigh(): number {
    return high;
  }
}

لم إعادة التصميم؟

قد لا يوفِّر الوصول المباشر إلى الحقل الخاصِّ المرونة الكافية ببعض الأحيان، فلربما تحتاج إلى تهيئة قيمة الحقل (initiate) عند أول استعلامٍ (query) يُجرَى على الحقل، أو تنفيذِ عمليّات معيّنة على القيم الجديدة المُسندة إلى الحقل حال إسنادها، أو القيام بكلِّ هذا في الأصناف الفرعيّة (subclasses) لهذا الصنف.

فوائد تطبيق الحل

  • تحقيق الوصول غير المباشر للحقل (عبر تابعي الوصول getter و setter)، وبهذا تضمن مرونةً أكبر للتعامل مع الحقل الخاصّ، إذ أصبح بالإمكان:
  1. إجراء العمليات المعقَّدة عند الحصول على قيمة الحقل أو تعديلها، ومن السهل كذلك القيام بالتهيئة الكسولة (lazy initialization) والتأكد من قيمة الحقل (value validation) داخل تابعي الوصول getter وsetter.
  2. إعادة تعريف التابعين getter و setter في الأصناف الفرعيّة (subclasses).
  • قد لا ترى بضرورة إنشاء تابع setter للحقل المطلوب، وعندها سيقتصر إسناد القيم لهذا الحقل على الباني (constructor) فقط، أي سيصبح الحقل غير قابلٍ للتغيير طيلة حياة الكائن (object).

مساوئ تطبيق الحل

لا شكَّ بأنّ الشيفرة تصبح أبسط بالاعتماد على الوصول المباشر للحقل بدلًا من تغليفه، ولكنّه ليس الخيار الأفضل من ناحية المرونة (flexibility).

آلية الحل

  1. إنشاء تابعٍ جالب getter (وربما ضابط setter إن دعت الحاجة) للحقل، وإمّا أن يكون مُحدِّد الوصول للتابع من النوع المحمي (protected) أو العام (public).
  2. تبديل كلِّ وصولٍ مباشرٍ للحقل إلى استدعاءٍ للتابع getter أو setter بحسب الحاجة.

انظر أيضًا

مصادر