الغرض من واجهات Zope؟
-
22-09-2019 - |
سؤال
لقد بدأت في استخدام واجهات Zope في الكود الخاص بي ، وحتى الآن ، فهي في الحقيقة وثائق فقط. أستخدمها لتحديد السمات التي يجب أن تمتلكها الفصل ، وتنفيذها بشكل صريح في الفئات المناسبة والتحقق منها صراحةً حيث أتوقع واحدة. هذا أمر جيد ، لكنني أود منهم أن يفعلوا المزيد إن أمكن ، مثل التحقق بالفعل من أن الفصل قد نفذ الواجهة ، بدلاً من التحقق من أنني قلت إن الفصل ينفذ الواجهة. لقد قرأت Zope Wiki عدة مرات ، لكن ما زلت لا أستطيع أن أرى استخدامًا أكبر بكثير للواجهات مما أقوم به حاليًا. لذا ، سؤالي هو ماذا يمكنك استخدام هذه الواجهات ، وكيف تستخدمها للمزيد.
المحلول
يمكنك بالفعل اختبار ما إذا كان الكائن أو الفصل الخاص بك ينفذ الواجهة الخاصة بك. لذلك يمكنك استخدامه verify
الوحدة النمطية (عادة ما تستخدمها في اختباراتك):
>>> from zope.interface import Interface, Attribute, implements
>>> class IFoo(Interface):
... x = Attribute("The X attribute")
... y = Attribute("The Y attribute")
>>> class Foo(object):
... implements(IFoo)
... x = 1
... def __init__(self):
... self.y = 2
>>> from zope.interface.verify import verifyObject
>>> verifyObject(IFoo, Foo())
True
>>> from zope.interface.verify import verifyClass
>>> verifyClass(IFoo, Foo)
True
يمكن أيضًا استخدام واجهات لإعداد واختبار الثوابات. يمكنك العثور على مزيد من المعلومات هنا:
نصائح أخرى
حيث أعمل ، نستخدم واجهات حتى نتمكن من استخدام ZCA ، أو بنية مكون Zope, ، وهو إطار كامل لصنع المكونات التي يمكن تبديلها وقابل للتبديل باستخدام Interface
س. نحن نستخدم ZCA حتى نتمكن من التعامل مع جميع أنواع التخصيصات لكل العميل دون الحاجة بالضرورة إلى توتر برامجنا أو الحصول على جميع الأجزاء العديدة لكل عميل تعبث الشجرة الرئيسية. غالبًا ما يكون Zope Wiki غير مكتمل تمامًا ، لسوء الحظ. هناك شرح جيد ولكنه يرتفع لمعظم ميزات ZCA على صفحة pypi من ZCA.
أنا لا أستخدم Interface
S لأي شيء مثل التحقق من أن الفئة تنفذ جميع طرق المعطى Interface
. من الناحية النظرية ، قد يكون ذلك مفيدًا عند إضافة طريقة أخرى إلى واجهة ، للتحقق من أنك تذكرت إضافة الطريقة الجديدة إلى جميع الفئات التي تنفذ الواجهة. أنا شخصياً أفضل إنشاء جديد Interface
على تعديل واحد قديم. تعديل القديم Interfaces
عادة ما تكون فكرة سيئة للغاية بمجرد أن تكون في البيض تم إصدارها إلى PYPI أو إلى بقية مؤسستك.
ملاحظة سريعة على المصطلحات: الفصول ينفذ Interface
s ، والأشياء (مثيلات الفصول) تزود Interface
س. إذا كنت تريد التحقق من الحصول على Interface
, ، إما أن تكتب ISomething.implementedBy(SomeClass)
أو ISomething.providedBy(some_object)
.
لذلك ، وصولاً إلى أمثلة على مكان ZCA مفيد. دعنا ندعي أننا نكتب مدونة ، نستخدم ZCA لجعلها معيارية. سيكون لدينا BlogPost
كائن لكل منشور ، والذي سيوفر IBlogPost
الواجهة ، كلها محددة في هويتنا المفيدة my.blog
بيضة. سنقوم أيضًا بتخزين تكوين المدونة في BlogConfiguration
الكائنات التي توفر IBlogConfiguration
. باستخدام هذا كنقطة انطلاق ، يمكننا تنفيذ ميزات جديدة دون الحاجة بالضرورة إلى لمسها my.blog
على الاطلاق.
فيما يلي قائمة بأمثلة للأشياء التي يمكننا القيام بها باستخدام ZCA ، دون الحاجة إلى تغيير القاعدة my.blog
بيضة. لقد قمت أنا أو زملائي في العمل بكل هذه الأشياء (ووجدتها مفيدة) في مشاريع حقيقية من أجل العميل ، على الرغم من أننا لم ننفذ المدونات في ذلك الوقت. :) يمكن حل بعض حالات الاستخدام هنا بشكل أفضل بوسائل أخرى ، مثل ملف CSS طباعة.
إضافة طرق عرض إضافية (
BrowserView
S ، عادة مسجلة في ZCML مع الbrowser:page
التوجيه) لجميع الكائنات التي توفرIBlogPost
. يمكنني جعل ملفmy.blog.printable
بيضة. أن البيضة ستسجل لعبة Browserviewprint
لIBlogPost
, ، مما يجعل منشور المدونة من خلال قالب صفحة Zope مصمم لإنتاج HTML الذي يطبع بشكل جيد. الذي - التيBrowserView
سوف تظهر بعد ذلك في عنوان URL/path/to/blogpost/@@print
.آلية الاشتراك في الحدث في Zope. لنفترض أنني أريد نشر خلاصات RSS ، وأريد إنشاءها مقدمًا بدلاً من الطلب. يمكنني إنشاء ملف
my.blog.rss
بيضة. في تلك البيضة ، سأقوم بتسجيل مشترك في الأحداث التي توفر iobjectmodified (zope.lifecycleevent.interfaces.IObjectModified
) ، على الأشياء التي توفرIBlogPost
. سيتم استدعاء هذا المشترك في كل مرة تتغير فيها سمة على أي شيء توفيرIBlogPost
, ، ويمكنني استخدامه لتحديث جميع خلاصات RSS التي يجب أن تظهر فيها منشور المدونة.في هذه الحالة ، قد يكون من الأفضل الحصول على
IBlogPostModified
الحدث الذي يتم إرساله في نهاية كل منBrowserView
S التي تعدل منشورات المدونة ، منذ ذلك الحينIObjectModified
يتم إرسالها مرة واحدة على كل تغيير سمة واحدة - والتي قد تكون في كثير من الأحيان من أجل الأداء.المحولات. المحولات هي "يلقي" بشكل فعال من واجهة إلى أخرى. بالنسبة إلى مهامو لغة البرمجة: تنفذ محولات Zope "مفتوحة" متعددة في Python (عن طريق "Open" أعني "يمكنك إضافة المزيد من الحالات من أي بيضة") ، مع تولي واجهة أكثر تحديدًا الأولوية على المباريات الأقل خصوصية (
Interface
يمكن أن تكون الفصول الدراسية فئات فرعية لبعضها البعض ، وهذا يفعل بالضبط ما تريد أن يفعله.)محولات من واحد
Interface
يمكن استدعاؤها مع بناء جملة لطيف للغاية ،ISomething(object_to_adapt)
, ، أو يمكن البحث عنها عبر الوظيفةzope.component.getAdapter
. محولات من متعددةInterface
يجب أن يتم البحث عن الوظيفة عبر الوظيفةzope.component.getMultiAdapter
, ، وهو أقل قليلا جميلة.يمكنك الحصول على أكثر من محول واحد لمجموعة معينة من
Interface
S ، متباينة بسلسلةname
التي تقدمها عند تسجيل المحول. الاسم الافتراضي ل""
. علي سبيل المثال،BrowserView
S هي في الواقع محولات تتكيف من الواجهة التي يتم تسجيلها عليها وواجهة تنفذها فئة HTTPrequest. يمكنك أيضا البحث الكل من المحولات المسجلة من تسلسل واحدInterface
لآخرInterface
, ، استخدامzope.component.getAdapters( (IAdaptFrom,), IAdaptTo )
, ، الذي يعيد سلسلة من أزواج (الاسم ، المحول). يمكن استخدام ذلك كوسيلة لطيفة للغاية لتوفير السنانير للإضافات لإرفاقها بها.لنفترض أنني أردت حفظ جميع منشورات مدونتي وتكوينها كملف XML كبير واحد. أنا إنشاء
my.blog.xmldump
البيض الذي يحددIXMLSegment
, ويسجل محول منIBlogPost
لIXMLSegment
ومحول منIBlogConfiguration
لIXMLSegment
. يمكنني الآن استدعاء أي محول مناسب لبعض الأشياء التي أرغب في التسلسل بها عن طريق الكتابةIXMLSegment(object_to_serialize)
.يمكنني حتى إضافة المزيد من المحولات من أشياء أخرى مختلفة إلى
IXMLSegment
من البيض بخلافmy.blog.xmldump
. يحتوي ZCML على ميزة حيث يمكنها تشغيل توجيه معين إذا وفقط إذا تم تثبيت بعض البيض. يمكنني استخدام هذا لmy.blog.rss
تسجيل محول منIRSSFeed
لIXMLSegment
IFFmy.blog.xmldump
يحدث أن يتم تثبيته ، دون صنعmy.blog.rss
يعتمد علىmy.blog.xmldump
.Viewlet
S مثل القليلBrowserView
S يمكن أن يكون لديك "الاشتراك" في بقعة معينة داخل صفحة. لا أستطيع أن أتذكر كل التفاصيل في الوقت الحالي ، لكن هذه جيدة جدًا لأشياء مثل الإضافات التي تريد الظهور في الشريط الجانبي.لا أستطيع أن أتذكر ما إذا كانت جزءًا من القاعدة Zope أو Plone. أود أن أوصي بعدم استخدام PLONE ما لم تكن المشكلة التي تحاول حلها تحتاج فعليًا إلى CMS حقيقي ، لأنها جزء كبير ومعقد من البرامج وتميل إلى أن تكون بطيئة نوعًا ما.
أنت لا تحتاج بالضرورة في الواقع
Viewlet
S على أي حال ، منذ ذلك الحينBrowserView
يمكن أن يتصل s ببعضهم البعض ، إما باستخدام "Object/some_browser_view" في تعبير TAL ، أو باستخدامqueryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' )
, ، لكنها لطيفة جدا بغض النظر.علامة
Interface
س. علامةInterface
هوInterface
التي لا توفر أي طرق ولا سمات. يمكنك إضافة علامةInterface
أي كائن في وقت التشغيل باستخدامISomething.alsoProvidedBy
. يتيح لك ذلك ، على سبيل المثال ، تغيير المحولات التي سيتم استخدامها على كائن معين وأيهاBrowserView
سيتم تعريف S على ذلك.
أعتذر أنني لم أخوض في التفاصيل الكافية لأتمكن من تنفيذ كل من هذه الأمثلة على الفور ، لكنهم كانوا يأخذون نشرًا تقريبًا لكل مدونة.
يمكن أن توفر واجهات Zope طريقة مفيدة لفصل قطعتين من التعليمات البرمجية التي لا ينبغي أن تعتمد على بعضها البعض.
قل أن لدينا مكونًا يعرف كيفية طباعة تحية في الوحدة النمطية:
>>> class Greeter(object):
... def greet(self):
... print 'Hello'
وبعض التعليمات البرمجية التي تحتاج إلى طباعة تحية في الوحدة النمطية B.Py:
>>> Greeter().greet()
'Hello'
هذا الترتيب يجعل من الصعب تبديل الكود الذي يتولى التحية دون لمس B.Py (والذي يمكن توزيعه في حزمة منفصلة). بدلاً من ذلك ، يمكننا تقديم وحدة ثالثة C.Py التي تحدد واجهة Igreeter:
>>> from zope.interface import Interface
>>> class IGreeter(Interface):
... def greet():
... """ Gives a greeting. """
الآن يمكننا استخدام هذا لفصل A.Py و B.Py. بدلاً من إنشاء فئة تحية ، سيطلب B.Py الآن فائدة توفر واجهة Igreeter. وسيعلن A.Py أن فئة الترحيب تنفذ تلك الواجهة:
(a.py)
>>> from zope.interface import implementer
>>> from zope.component import provideUtility
>>> from c import IGreeter
>>> @implementer(IGreeter)
... class Greeter(object):
... def greet(self):
... print 'Hello'
>>> provideUtility(Greeter(), IGreeter)
(b.py)
>>> from zope.component import getUtility
>>> from c import IGreeter
>>> greeter = getUtility(IGreeter)
>>> greeter.greet()
'Hello'
لم أستخدم واجهات Zope مطلقًا ، ولكن قد تفكر في كتابة ملف metaclass, ، الذي يتحقق من التهيئة من أعضاء الفصل ضد الواجهة ، ويثير استثناء وقت التشغيل إذا لم يتم تنفيذ الطريقة.
مع Python ليس لديك خيارات أخرى. إما أن لديها خطوة "ترجمة" تفقد الكود الخاص بك ، أو فحصه ديناميكيًا في وقت التشغيل.