0

作为输入,我有一个书籍列表。作为输出,我期望一个 SimilarBookCollection。

SimilarBookCollection 有作者、publishYear 和图书列表。如果书籍的作者不同或 publishYear 不同,则无法创建 SimilarBookCollection。

到目前为止在 PHP 中的解决方案:

client.php
----
$arrBook = array(...); // array of books
$objValidator = new SimilarBookCollectionValidator($arrBook);
if ($objValidator->IsValid()) {
   $objSimilarBookCollection = new SimilarBookCollection($arrBook);
   echo $objSimilarBookCollection->GetAuthor();
}
else {
   echo 'Invalid input';
}


SimilarBookCollection.php
---
class SimilarBookCollection() {
 public function SimilarBookCollection(array $arrBook) { 
       $objValidator = new SimilarBookCollectionValidator($arrBook); 
       if ($objValidator->IsValid()) {
         throw new Exception('Invalid books to create collection');
       }
       $this->author = $arrBook[0]->GetAuthor();
       $this->publishYear = $arrBook[0]->GetPublishYear();
       $this->books = $arrBook;
 }
 public function GetAuthor() {
      return $this->author;
 }

 public function GetPublishYear() {
      return $this->publishYear;
 }

 public function GetBooks() {
      return $this->books;
 }
}

SimilarBookCollectionValidator.php
---
class SimilarBookCollectionValidator() {
 public function IsValid() {
   $this->ValidateAtLeastOneBook();
   $this->ValidateSameAuthor();
   $this->ValidateSameYear();

   return $this->blnValid;
 }

 ... //actual validation routines
}

目标是拥有一个“特殊”收藏,其中仅包含具有相同作者和发布年份的书籍。这个想法是从对象中轻松访问重复信息,如作者或年份。

  • 您如何命名 SimilarBookCollection?当前名称是通用的。使用 SameYearAuthorBookCollection 之类的名称看起来有点长且奇怪(如果添加更多条件,则名称会增加)

  • 您会使用防御性编程风格在 SimilarBookCollection 构造函数中使用验证器吗?

  • 你会改变代码的设计吗?如果是怎么办?

4

1 回答 1

0

这一切都取决于;)

因此,如果我要寻求通用的适应性解决方案,我会执行以下操作:

构造函数中的验证器 一方面,您要验证两次;如果先决条件/合同被破坏(没有给出有效的列表),这会提供信息,但是要运行的代码是双倍的——究竟是为了什么目的?如果您想在系统中使用它,取决于它的大小、它的重要性、产品阶段以及可能的更多标准。但它也是适合模型的控制器逻辑,这意味着您正在传播您的代码。

我不会把它放在构造函数中。

名称/设计 我想说的是保持 BookCollection 的通用性,并严格在控制器空间中进行任何验证,而不是使集合膨胀,该集合本质上似乎是一个带有额外作者字段的数组。

如果您想区分不同的集合类型,请使用(多个)继承或某种附加字段“collectionType”;如果您希望出现许多衍生品或不同的功能(也保持不同的逻辑很好地分开),则为前者。

您还可以将您的集合视为执行查询的集合,为方便起见,您可以维护某种元数据,例如 $AuthorCount = N, $publicationDates = array(...) 从中您可以快速得出集合的性质. 这种方法还将使您的验证器代码最小化(或不存在),因为它隐含在集合中,您可以只在控制器中进行验证,保持其背后的有效逻辑清晰可见。

这也将使您将来更加舒适。但问题实际上是您想要什么和需要它,以及您期望什么变化,因为您应该使您的设计符合您的要求和可能的变化。

对于您非常特殊的问题,据我所知,约束如下:

  1. 在任何给定时间点,系统中只有一个集合类型类。
  2. 类的项目有几个属性,对于这些的一个特定的、可能变化的子集(称为相同属性),该集合只接受所有项目的所选属性相同的项目列表。
  3. 该类为所有相同的属性提供 getter
  4. 该类不得以预期方式以外的任何其他方式使用。

如果不是第 1 点,我会使用一个通用基类,它要么是参数化的(即你在实例化时告诉它这是一组相同的属性),要么使用多重继承(或在 php 特征中)与所需的接口组成任意组合。子类可能依赖于基类,但使用相同属性的预定义子集。

参数化变体可能如下所示:

class BookCollection {
    public function __construct($book_list, $identical_fields=array())
    {
         if (empty($book_list))
         {
             throw new EmptyCollectionException("Empty book list");
         }

         $default = $book_list[0];
         $this->ia = array();
         foreach($identical_fields as $f)
         {
              $this->ia[$f] = $default->$f;
         }

         foreach($book_list as $book)
         {
              foreach($identical_fields as $f)
              {
                   if ($this->ia[$f] !== $book->$f)
                   {
                        throw new NotIdenticalFieldException("Field $f is not identical for all");
                   }
               }
          }

          $this->book_list = $book_list;
    }
    public function getIdentical($key)
    {
        $this->ia[$key];
    }
}

final class BC_by_Author extends BookCollection{
    public function __construct($book_list)
    {
        parent::__construct($book_list,array('author'));
    }

    public function getAuthor(){ $this->ia['author']; }
}

或玩弄抽象和最终类型(不确定它是否像这样有效)

abstract class BookCollection{
    public final function __construct($book_list){...}
    abstract public function getIdenticalAttributes();
}
final class BC_by_Author {
    public function getIdenticalAttributes(){ return array('author'); }
    public function getAuthor(){ return $this->ia['author']; }
}

如果您依赖的 getter 不一定与字段名称匹配,我会选择多重继承/特征。然后命名将类似于 BC_Field1Field2Field3。

或者或另外,您也可以使用完全相同的类名,但在不同的命名空间中开发您的解决方案,这意味着您在更改命名空间时不必更改代码,而且您可以在控制器中保持简短。

但是因为永远只有一个类,所以我将它命名为 BookCollection 并且不再不必要地讨论它。

由于约束 4,白盒约束,给定的书单必须由类本身验证,即在构造函数中。

于 2013-10-09T18:53:18.593 回答