النمط المركّب
جزء من قسم البرمجة كائنية التوجّه في رحلة GDScript على Coddy. الدرس 55 من 65.
ينشئ نمط Composite أشجارًا يكون فيها لكائن واحد ولمجموعة من الكائنات الأساليب نفسها، لذا يمكن للكود التعامل مع مجموعة كاملة كما لو كانت عنصرًا واحدًا. تحتوي الحقيبة على عناصر، ويمكن للحقيبة أن تحتوي على حقائب أخرى، ويمكنك أن تسأل أيًّا منها عن وزنها بالطريقة نفسها.
يشترك كلا النوعين في صنف أب مجرّد:
# entry.gd
@abstract
class_name Entry
extends RefCounted
var entry_name: String
@abstract func get_weight() -> int
@abstract func display(indent: String) -> voidالعنصر الطرفي هو عنصر واحد:
# item.gd
class_name Item
extends Entry
var weight: int
func _init(n: String, w: int) -> void:
entry_name = n
weight = w
func get_weight() -> int:
return weight
func display(indent: String) -> void:
print(indent, entry_name, " (", weight, ")")يحتوي المركّب على إدخالات أخرى، ويجيب عن طريق سؤال كلٍّ منها. يزن الكيس 1 بحد ذاته:
# bag.gd
class_name Bag
extends Entry
var contents: Array[Entry] = []
func _init(n: String) -> void:
entry_name = n
func add(entry: Entry) -> void:
contents.append(entry)
func get_weight() -> int:
var total := 1
for entry in contents:
total += entry.get_weight()
return total
func display(indent: String) -> void:
print(indent, entry_name, " [", get_weight(), "]")
for entry in contents:
entry.display(indent + " ")# main.gd
var pouch := Bag.new("Pouch")
pouch.add(Item.new("Gem", 2))
var backpack := Bag.new("Backpack")
backpack.add(Item.new("Sword", 8))
backpack.add(pouch)
backpack.display("")Backpack [12]
Sword (8)
Pouch [3]
Gem (2)لا تتحقق Bag.get_weight() مطلقًا مما إذا كان المُدخل عنصرًا أم حقيبة؛ فهي تستدعي الطريقة نفسها على كلٍّ منهما، والحقيبة الموجودة داخلها تجيب عن طريق الاستعلام عن محتوياتها. يتنقل هذا الاستدعاء الذاتي عبر الشجرة بأكملها، مهما بلغ عمقها.
تسير الروابط في اتجاه واحد، من الحقيبة إلى محتوياتها. إذا خزّن كل إدخال أيضًا الحقيبة التي يوجد فيها، فسيشير كلٌّ من الحقيبة وإدخالاتها إلى الآخر، مكوّنين دورة مراجع لا تُحرَّر أبدًا. عندما يحتاج إدخال إلى معرفة موقعه، مرّر هذه المعلومة إليه كوسيط، بالطريقة التي تمرّر بها display() مستوى الإزاحة.
التحدي
متوسطأنشئ شجرة مجلدات باستخدام نمط المركب. يعرّف DiskEntry (disk_entry.gd) الدوال get_size() وcount_files() وlist_lines(indent). اكتب ما يلي:
FileEntry: حجمه هوsizeالخاص به، ويُحتسب ملفًا واحدًا1، وتطبعlist_lines()المسافة البادئة، ثمNAME (SIZE KB).FolderEntry: تخزّنadd(entry)عنصرًا فرعيًا. حجمه هو مجموع أحجام العناصر الفرعية، وعدد ملفاته هو مجموع أعداد ملفاتها، وتطبعlist_lines()المسافة البادئة، ثمNAME/ (SIZE KB)، ثم تسرد كل عنصر فرعي مع إضافة مسافتين إلى المسافة البادئة.
تنشئ الشيفرة المتوفرة الشجرة من الإدخال (عدد الأسطر التي تلي ذلك، ثم folder NAME أو file NAME SIZE أو end، الذي يغلق المجلد الحالي)، وتسردها وتطبع الإجماليات. بالنسبة إلى مجلد Root يحتوي على notes.txt (4 KB) ومجلد Music يحتوي على song.mp3 (300 KB)، يكون الناتج:
Root/ (304 KB)
notes.txt (4 KB)
Music/ (300 KB)
song.mp3 (300 KB)
Total: 304 KB, files: 2ضع شيفرتك في file_entry.gd وfolder_entry.gd. يحتوي main.gd على شيفرة الإدخال والإخراج المتوفرة، ولا يمكن تعديله.
جرّب بنفسك
extends Node
func _ready():
# كود الإدخال والإخراج المقدَّم: اتركه كما هو
var open_folders: Array[FolderEntry] = []
var root: FolderEntry = null
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
match parts[0]:
"folder":
var folder := FolderEntry.new(parts[1])
if open_folders.is_empty():
root = folder
else:
open_folders.back().add(folder)
open_folders.append(folder)
"file":
open_folders.back().add(FileEntry.new(parts[1], int(parts[2])))
"end":
open_folders.pop_back()
root.list_lines("")
print("Total: %d KB, files: %d" % [root.get_size(), root.count_files()])
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجّه
1أساسيات البرمجة كائنية التوجه
الملفات الخارجيةمقدمة إلى البرمجة كائنية التوجهالفئات مقابل الكائناتالكلمة المفتاحية selfالأساليبالخصائصالمُنشئ (_init)مراجعة - آلة حاسبة بسيطة4تعدد الأشكال
إعادة النظر في تجاوز الأساليبكتابة الأنواع بأسلوب البطةالفئات والأساليب المجرّدةتصميم الواجهاتمراجعة - حاسبة الأشكال10أنماط التصميم، الجزء الثاني
نمط الأوامرنمط المحوّلنمط المُزيّننمط الطريقة القالبيةنمط الحالةالنمط المركّب2خصائص الأصناف
متغيرات الكائن مقابل المتغيرات الساكنةالدوال الساكنةمُعيِّنات الخصائصمُسترجِعات الخصائصخاص وفقًا للاتفاقيةمراجعة - مدير الحسابات البنكية5التغليف
الأعضاء العامة والخاصةالتحقق من الصحة باستخدام دوال الضبطإخفاء المعلوماتالخصائص للقراءة فقطمراجعة - نظام سجلات الطلاب8مفاهيم متقدمة في البرمجة كائنية التوجه
التركيب مقابل الوراثةالأصناف الداخليةالعُقد كمكوّناتRefCounted مقابل Nodeالتعدادات في الأصنافتدرّب بنفسك: مترجم GDScript عبر الإنترنت