تغليف المنطق المشترك (التصميم القائم على المجال، وأفضل الممارسات)

StackOverflow https://stackoverflow.com/questions/515608

سؤال

محدث: 02/09/2009 - تمت مراجعة السؤال، وقدم أمثلة أفضل، ومكافأة إضافية.


أهلاً،
أقوم بإنشاء تطبيق PHP باستخدام نمط مخطط البيانات بين قاعدة البيانات والكيانات (كائنات المجال).سؤالي هو:

ما هي أفضل طريقة لتغليف مهمة يتم تنفيذها بشكل شائع؟

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

$siteMapper = new Site_Mapper();
$site = $siteMapper->findByid(1);

$pageMapper = new Page_Mapper();
$site->addPage($pageMapper->findHome($site->getId()));

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

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

$someObject = new SomeClass();
$site = $someObject->someMethod(1); // or
$sites = $someObject->someOtherMethod();

حيث أن كيانات الموقع الناتجة قد تم بالفعل إنشاء كياناتها المرتبطة بها وجاهزة للاستخدام.

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

$siteMapper->save($site);
$pageMapper->save($site->getHomePage());

مرة أخرى، تافهة، ولكن هذا المثال مبسط.لا يزال تكرار التعليمات البرمجية ساريًا.

في رأيي، من المنطقي أن يكون هناك كائن مركزي يمكنه الاهتمام بما يلي:

  • استرجاع الموقع (أو المواقع) وجميع الكيانات المرتبطة به الضرورية
  • إنشاء كيانات موقع جديدة مع كيانات مرتبطة جديدة
  • أخذ موقع (أو مواقع) وحفظه وجميع الكيانات المرتبطة به (إذا كانت قد تغيرت)

لذا أعود إلى سؤالي، ما هو هذا الكائن؟

  • كائن معين موجود؟
  • شيء يعتمد على نمط المستودع؟*
  • شيء يعتمد على وحدة العمل؟*
  • شيء آخر؟

* أنا لا تماما فهم أيًا منهما، كما يمكنك تخمينه على الأرجح.

هل هناك طريقة قياسية للتعامل مع هذه المشكلة، وهل يمكن لأي شخص أن يقدم وصفًا موجزًا ​​لكيفية تنفيذها؟أنا لا أبحث عن أي شخص ليقدم تنفيذًا عمليًا بالكامل، فقط النظرية.

شكرًا،
جاك

هل كانت مفيدة؟

المحلول

باستخدام نمط المستودع/الخدمة، ستوفر فئات المستودع الخاصة بك واجهة CRUD بسيطة لكل كيان من كياناتك، ثم ستكون فئات الخدمة طبقة إضافية تؤدي منطقًا إضافيًا مثل إرفاق تبعيات الكيان.ثم يستخدم باقي تطبيقك الخدمات فقط.المثال الخاص بك قد يبدو مثل هذا:

$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();

ثم داخل فئة SiteService سيكون لديك شيء مثل هذا:

function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}

لا أعرف لغة PHP جيدًا لذا يرجى المعذرة إذا كان هناك خطأ نحوي.

نصائح أخرى

[يحرر:يحاول هذا الإدخال معالجة حقيقة أنه في كثير من الأحيان يكون من الأسهل كتابة تعليمات برمجية مخصصة للتعامل مباشرة مع الموقف بدلاً من محاولة ملاءمة المشكلة في نمط ما.]

الأنماط جميلة من حيث المفهوم، لكنها لا "ترسم خريطة" دائمًا.بعد سنوات من التطوير المتطور لـ PHP، استقرينا على طريقة مباشرة جدًا للتعامل مع مثل هذه الأمور.النظر في هذا:

ملف:الموقع.php

class Site
{
   public static function Select($ID)
   {
      //Ensure current user has access to ID
      //Lookup and return data
   }

   public static function Insert($aData)
   {
      //Validate $aData
      //In the event of errors, raise a ValidationError($ErrorList)

      //Do whatever it is you are doing

      //Return new ID
   }

   public static function Update($ID, $aData)
   {
      //Validate $aData
      //In the event of errors, raise a ValidationError($ErrorList)

      //Update necessary fields
   }

ثم، من أجل الاتصال به (من أي مكان)، فقط قم بتشغيل:

$aData = Site::Select(123);

Site::Update(123, array('FirstName' => 'New First Name'));

$ID = Site::Insert(array(...))

هناك شيء واحد يجب مراعاته حول برمجة OO وPHP...لا تحتفظ PHP بـ "الحالة" بين الطلبات، لذا فإن إنشاء مثيل كائن لمجرد تدميره على الفور ليس له معنى في كثير من الأحيان.

ربما سأبدأ باستخراج المهمة الشائعة إلى طريقة مساعدة في مكان ما، ثم الانتظار لمعرفة ما يتطلبه التصميم.يبدو أنه من السابق لأوانه معرفة ذلك.

ماذا ستسمي هذه الطريقة؟يشير الاسم عادةً إلى المكان الذي تنتمي إليه الطريقة.

class Page {

  public $id, $title, $url;

  public function __construct($id=false) {
    $this->id = $id;
  }

  public function save() {
    // ...
  }

}

class Site {

  public $id = '';
  public $pages = array();

  function __construct($id) {
     $this->id = $id;
     foreach ($this->getPages() as $page_id) {
       $this->pages[] = new Page($page_id);
     }
  }

  private function getPages() {
    // ...
  }

  public function addPage($url) {
    $page = ($this->pages[] = new Page());
    $page->url = $url;
    return $page;
  }

  public function save() {
    foreach ($this->pages as $page) {
      $page->save();
    } 
    // ..
  }

}

$site = new Site($id);
$page = $site->addPage('/');
$page->title = 'Home';
$site->save();

اجعل كائن موقعك الجذر الكلي لتغليف الارتباط المعقد وضمان الاتساق.

ثم قم بإنشاء مستودع الموقع التي تتحمل مسؤولية استرداد إجمالي الموقع وملء أبنائه (بما في ذلك جميع الصفحات).

لن تحتاج إلى PageRepository منفصل (على افتراض أنك لا تجعل الصفحة جذرًا مجمعًا منفصلاً)، ويجب أن يتحمل SiteRepository الخاص بك مسؤولية استرداد كائنات الصفحة أيضًا (في حالتك باستخدام مصممي الخرائط الحاليين).

لذا:

$siteRepository = new SiteRepository($myDbConfig);
$site = $siteRepository->findById(1); // will have Page children attached

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

يحرر: إليك دليل DDD قصير لمساعدتك في المصطلحات، على الرغم من أنني أوصي حقًا بالقراءة إيفانز إذا كنت تريد الصورة كاملة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top