查看您的代码,您肯定有一个良好的开端。在 OOP 中编程时,您已经使用了一条好的经验法则 -对接口进行编程,而不是对实现进行编程。按术语界面我不仅指实际的接口,还指抽象类。
所以你的问题的核心是你想要上两门课,ClassA
and ClassB
两者都使用通用方法RequestReferrerHandler
。您已经为界面做好了基础工作RequestReferrerHandlerInterface
。所以我们会说你有一个如下所示的界面:
interface RequestReferrerHandlerInterface
{
public function hasAuthorReferrerValue();
public function hasDateReferrerValue();
public function hasSearchReferrerValue();
public function hasTaxReferrerValue();
}
只要这个接口是由实现的RequestReferrerHandler
您可以键入提示接口作为构造函数的要求ClassA
and ClassB
。但这不是什么新鲜事,因为您已经这样做了。
有两件事特别让我觉得潜在的麻烦。首先,由于您希望类的职责较小,因此您应该承担向类提供数据的责任RequestReferrerHandler
远离它自己并把它交给你Controller
。换句话说,不要注入$_GET
进入你的班级。确保您的Controller
拥有正确创建所需的所有信息RequestReferrerHandler
让我们看看您的RequestReferrerHandler
类,充满了它需要的所有方法。
class RequestReferrerHandler implements RequestReferrerHandlerInterface
{
private $author;
private $date;
private $search;
private $tax;
public function __construct($author = null, $date = null, $search = null, $tax = null)
{
$this->setAuthorReferrer($author);
$this->setDateReferrer($date);
$this->setSearchReferrer($search);
$this->setTaxReferrer($tax);
}
public function hasAuthorReferrerValue()
{
return $this->author !== null ? true : false;
}
public function hasDateReferrerValue()
{
return $this->date !== null ? true : false;
}
public function hasSearchReferrerValue()
{
return $this->search !== null ? true : false;
}
public function hasTaxReferrerValue()
{
return $this->tax !== null ? true : false;
}
public function getAuthorReferrer()
{
return $this->author;
}
public function getDateReferrer()
{
return $this->date;
}
public function getSearchReferrer()
{
return $this->search;
}
public function getTaxReferrer()
{
return $this->tax;
}
public function setAuthorReferrer($author)
{
$this->author = $author;
}
public function setDateReferrer($date)
{
$this->date = $date;
}
public function setSearchReferrer($search)
{
$this->search = $search;
}
public function setTaxReferrer($tax)
{
$this->tax = $tax;
}
}
第二个突出的事情是santize()
方法。你看到它们是如何在两者中重复的吗?ClassA
and ClassB
? The sanitizeAuthor()
两个类之间有所不同,但是其余的呢?这是 DRY 的情况(不要重复自己)原则可以提供帮助。由于多个类可能必须以类似的方式清理数据,因此将其从类中抽象出来是有意义的。
让我们看看如何做到这一点,然后我们将回到您的具体课程。首先创建一个新接口,该接口将指定必须由可以清理数据的对象公开的方法。
interface SanitizerInterface
{
public function sanitizeAuthor();
public function sanitizeDate();
public function sanitizeSearch();
public function sanitizeTaxonomy();
}
现在,如果你拥有的每一个物体ClassX
以不同的方式实现这四种方法,您可以开始在不同的类中实现它,这些类只是清理数据。然而,对于这个例子,我们会说情况并非如此。让我们假设sanitizeAuthor()
之间可能有所不同ClassA
and ClassB
(它在您的代码中)并且所有其他方法将完全相同地实现。在这种情况下,我们可以使用一个抽象类来实现消毒剂方法。
abstract class AbstractSanitizer implements SanitizerInterface
{
protected $handler;
public function __construct() {}
public function setHandler(RequestReferrerHandlerInterface $handler)
{
$this->handler = $handler;
}
/* For this example we are saying that sanitizeDate(), sanitizeTaxonomy() and
* sanitizeSearch() will be the same no matter what. So let's implement them
* and leave the child classes to implement sanitizeAuthor().
*
* Implement the details of the sanitizer function to fit your needs.
*/
public function sanitizeDate()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the date
$sanitized = strtoupper($this->handler->getDateReferrer());
echo "Sanitize date -> switch to uppercase letters.\n";
$this->handler->setDateReferrer($sanitized);
}
}
public function sanitizeSearch()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the search
$sanitized = strtolower($this->handler->getSearchReferrer());
echo "Sanitize search -> switch to lowercase letters.\n";
$this->handler->setSearchReferrer($sanitized);
}
}
public function sanitizeTaxonomy()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the taxonomy
$sanitized = str_replace(" ", "_", $this->handler->getTaxReferrer());
echo "Sanitize Taxonomy -> convert spaces to underscores.\n";
$this->handler->setTaxReferrer($sanitized);
}
}
}
有些事情需要立即注意。首先,你会注意到有setHandler()
方法接受一个实例RequestReferrerHandlerInterface
。为什么会有这个?大部分都是方便的。既然我们已经采取了消毒行为并将其封装到自己的类中,那么如果我们为消毒器提供了一种更新具体行为的方法,那就太好了RequestReferrerHandler
它与清理方法的更新输出一起使用。
接下来,我们使用以下方法RequestReferrerHandler
中未指定的类RequestReferrerHandlerInterface
。这不是一个迫在眉睫的问题per se,因为我们知道像 getter 和 setter 这样的方法在类中。然而,仅对界面进行类型提示并不能保证如果您决定使用不同的具体对象来实现该接口,那么这些方法将可用。因此,我们需要更新RequestReferrerHandlerInterface
以及保证其可用性的方法。
interface RequestReferrerHandlerInterface
{
public function hasAuthorReferrerValue();
public function hasDateReferrerValue();
public function hasSearchReferrerValue();
public function hasTaxReferrerValue();
public function getAuthorReferrer();
public function getDateReferrer();
public function getSearchReferrer();
public function getTaxReferrer();
public function setAuthorReferrer($author);
public function setDateReferrer($date);
public function setSearchReferrer($search);
public function setTaxReferrer($tax);
}
现在,回到那些消毒剂。我们知道ClassA
and ClassB
将实施他们的sanitizeAuthor()
方法不同。抽象类AbstractSanitizer
之所以被做成这样是因为sanitizeAuthor()
方法从SanitizerInteface
未实施于AbstractSanitizer
所以我们必须扩展它以提供功能。我们需要以下两个类来执行此操作:
class SanitizerForClassA extends AbstractSanitizer
{
/* This class must provide an implementation for how ClassA will
* handle the sanitizeAuthor() method.
*/
public function sanitizeAuthor()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the for ClassA
$sanitized = array("author" => $this->handler->getAuthorReferrer());
echo "Sanitize author -> ClassA makes author an array.\n";
$this->handler->setAuthorReferrer($sanitized);
}
}
}
class SanitizerForClassB extends AbstractSanitizer
{
/* This class must provide an implementation for how ClassB will
* handle the sanitizeAuthor() method.
*/
public function sanitizeAuthor()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the for ClassB
$sanitized = new stdClass();
$sanitized->author = $this->handler->getAuthorReferrer();
echo "Sanitize author -> ClassB makes author an object property. \n";
$this->handler->setAuthorReferrer($sanitized);
}
}
}
这两个具体类可以与ClassA
and ClassB
净化混凝土中的数据RequestReferrerHandler
将传递给它们的方法。
那么继续,让我们看看规格ClassA
and ClassB
。我们知道ClassA
将需要该方法queryArguments()
, ClassB
将需要该方法queryVars()
并且两个类都需要允许一个实例RequestReferrerHandlerInterface
and SanitizerInterface
在他们的构造函数中。我们将使用一个接口来处理构造函数要求,然后另外两个接口将扩展该接口以提供所需的所有方法要求ClassA
and ClassB
.
interface SanitizableHandlerInterface
{
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer);
}
interface QueryVarsInterface extends SanitizableHandlerInterface
{
public function queryVars();
}
interface QueryArgumentsInterface extends SanitizableHandlerInterface
{
public function queryArguments();
}
既然我们现在要开始讨论它,那么让我们看一下将使用这些的类。
class ClassA implements QueryArgumentsInterface
{
private $handler;
private $sanitizer;
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
{
$this->handler = $handler;
$this->sanitizer = $sanitizer;
$this->sanitizer->setHandler($this->handler);
}
public function queryArguments() // Will be used in the controller class ClassC
{
$queryArgs = null;
if($this->handler->hasAuthorReferrerValue())
{
$this->sanitizer->sanitizeAuthor();
$queryArgs = $this->handler->getAuthorReferrer();
}
if($this->handler->hasDateReferrerValue())
{
$this->sanitizer->sanitizeDate();
$queryArgs = $this->handler->getDateReferrer();
}
if($this->handler->hasSearchReferrerValue())
{
$this->sanitizer->sanitizeSearch();
$queryArgs = $this->handler->getSearchReferrer();
}
if($this->handler->hasTaxReferrerValue())
{
$this->sanitizer->sanitizeTaxonomy();
$queryArgs = $this->handler->getTaxReferrer();
}
return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
class ClassB implements QueryVarsInterface
{
private $handler;
private $sanitizer;
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
{
$this->handler = $handler;
$this->sanitizer = $sanitizer;
$this->sanitizer->setHandler($this->handler);
}
public function queryVars() // Will be used in the controller class ClassC
{
$queryVars = null;
if($this->handler->hasAuthorReferrerValue())
{
$this->sanitizer->sanitizeAuthor();
$queryVars = $this->handler->getAuthorReferrer();
}
if($this->handler->hasDateReferrerValue())
{
$this->sanitizer->sanitizeDate();
$queryVars = $this->handler->getDateReferrer();
}
if($this->handler->hasSearchReferrerValue())
{
$this->sanitizer->sanitizeSearch();
$queryVars = $this->handler->getSearchReferrer();
}
if($this->handler->hasTaxReferrerValue())
{
$this->sanitizer->sanitizeTaxonomy();
$queryVars = $this->handler->getTaxReferrer();
}
return $queryVars; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
到这里,基础工作就完成了。您会注意到,在构造函数中,为给定的处理程序和消毒剂类设置了属性,然后为消毒剂提供了对处理程序的引用。(请记住,清理程序具有对处理程序的引用,以便处理程序中的清理属性会自动更新。各个类现在不需要担心这一点。)
所以现在最重要的问题是如何使用它。好吧,你需要一个可以接受的控制器ClassA
and ClassB
。我们也将通过它们各自的接口输入提示。
class Controller
{
public function __construct() {}
public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass)
{
var_dump($argsClass->queryArguments());
var_dump($varsClass->queryVars());
}
}
在你的版本中queryArguments()
and queryVars()
您期望返回值是经过清理的数据。让我们插入一些数据,看看会得到什么。(注意:正如您已经发现我使用的任何清理方法都没有做您正在做的事情,它们只是说明性的。)
//TEST DRIVE
//Create a controller that will use the classes
$controller = new Controller();
//Now make use of your new shiny handlers and sanitizers
$controller->doStuff(
new ClassA(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassB())
);
这是输出:
Sanitize author -> ClassA makes author an array.
array (size=1)
'author' => string 'Mark Twain' (length=10)
Sanitize date -> switch to uppercase letters.
string 'JANUARY 1ST, 1999' (length=17)
Sanitize search -> switch to lowercase letters.
string 'ok google now!' (length=14)
Sanitize Taxonomy -> convert spaces to underscores.
string 'Super_Awesome_Taxonomy_Tables' (length=29)
Sanitize date -> switch to uppercase letters.
string 'JANUARY 1ST, 1999' (length=17)
Sanitize author -> ClassB makes author an object property.
object(stdClass)[15]
public 'author' => string 'Mark Twain' (length=10)
Sanitize Taxonomy -> convert spaces to underscores.
string 'Super_Awesome_Taxonomy_Tables' (length=29)
Sanitize search -> switch to lowercase letters.
string 'ok google now!' (length=14)
那么这一切让你付出了什么代价?简短的回答 - 复杂性。它需要 4 个接口、1 个抽象类和一些具体类才能将一点数据输出到屏幕上。
你有什么收获?简短的回答——灵活性。将来您可能希望添加更多的类来实现QueryVarsInterface
or QueryArgumentsInterface
。考虑这些课程ClassC
, ClassD
and ClassE
。所有这些类都需要一个消毒剂类来配合它们(也就是说,如果SanitizerForClassA
or SanitizerForClassB
不符合要求)并且继续编写消毒剂类会很乏味。嗯,对你来说是件好事,因为你一直在对接口进行编程,你不会有这个问题。您可以轻松地制作一个GenericSanitizer
与默认实现sanitizeAuthor()
方法。使用可以将此类与Controller::doStuff()
在任何情况下,您都不需要专门的消毒剂课程。您可以轻松地实现不同的具体类QueryArgumentInterface
or QueryVarsInterface
测试您想要添加的实验功能而不篡改当前课程。
希望这能让您对一些 OOP 原则有所了解。这是上面所有代码的完整副本。将其放入一个空的 PHP 文件中并运行它以查看所有操作。快乐编程!
<?php
/*
* INTERFACES
*/
interface RequestReferrerHandlerInterface
{
public function hasAuthorReferrerValue();
public function hasDateReferrerValue();
public function hasSearchReferrerValue();
public function hasTaxReferrerValue();
public function getAuthorReferrer();
public function getDateReferrer();
public function getSearchReferrer();
public function getTaxReferrer();
public function setAuthorReferrer($author);
public function setDateReferrer($date);
public function setSearchReferrer($search);
public function setTaxReferrer($tax);
}
interface SanitizerInterface
{
public function sanitizeAuthor();
public function sanitizeDate();
public function sanitizeSearch();
public function sanitizeTaxonomy();
}
interface SanitizableHandlerInterface
{
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer);
}
interface QueryVarsInterface extends SanitizableHandlerInterface
{
public function queryVars();
}
interface QueryArgumentsInterface extends SanitizableHandlerInterface
{
public function queryArguments();
}
/*
* ABSTRACT CLASSES
*/
abstract class AbstractSanitizer implements SanitizerInterface
{
protected $handler;
public function __construct() {}
public function setHandler(RequestReferrerHandlerInterface $handler)
{
$this->handler = $handler;
}
/* For this example we are saying that sanitizeDate(), sanitizeTaxonomy() and
* sanitizeSearch() will be the same no matter what. So let's implement them
* and leave the child classes to implement sanitizeAuthor().
*
* Implement the details of the sanitizer function to fit your needs.
*/
public function sanitizeDate()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the date
$sanitized = strtoupper($this->handler->getDateReferrer());
echo "Sanitize date -> switch to uppercase letters.\n";
$this->handler->setDateReferrer($sanitized);
}
}
public function sanitizeSearch()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the search
$sanitized = strtolower($this->handler->getSearchReferrer());
echo "Sanitize search -> switch to lowercase letters.\n";
$this->handler->setSearchReferrer($sanitized);
}
}
public function sanitizeTaxonomy()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the taxonomy
$sanitized = str_replace(" ", "_", $this->handler->getTaxReferrer());
echo "Sanitize Taxonomy -> convert spaces to underscores.\n";
$this->handler->setTaxReferrer($sanitized);
}
}
}
/*
* CONCRETE CLASSES
*/
class RequestReferrerHandler implements RequestReferrerHandlerInterface
{
private $author;
private $date;
private $search;
private $tax;
public function __construct($author = null, $date = null, $search = null, $tax = null)
{
$this->setAuthorReferrer($author);
$this->setDateReferrer($date);
$this->setSearchReferrer($search);
$this->setTaxReferrer($tax);
}
public function hasAuthorReferrerValue()
{
return $this->author !== null ? true : false;
}
public function hasDateReferrerValue()
{
return $this->date !== null ? true : false;
}
public function hasSearchReferrerValue()
{
return $this->search !== null ? true : false;
}
public function hasTaxReferrerValue()
{
return $this->tax !== null ? true : false;
}
public function getAuthorReferrer()
{
return $this->author;
}
public function getDateReferrer()
{
return $this->date;
}
public function getSearchReferrer()
{
return $this->search;
}
public function getTaxReferrer()
{
return $this->tax;
}
public function setAuthorReferrer($author)
{
$this->author = $author;
}
public function setDateReferrer($date)
{
$this->date = $date;
}
public function setSearchReferrer($search)
{
$this->search = $search;
}
public function setTaxReferrer($tax)
{
$this->tax = $tax;
}
}
class SanitizerForClassA extends AbstractSanitizer
{
/* This class must provide an implementation for how ClassA will
* handle the sanitizeAuthor() method.
*/
public function sanitizeAuthor()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the for ClassA
$sanitized = array("author" => $this->handler->getAuthorReferrer());
echo "Sanitize author -> ClassA makes author an array.\n";
$this->handler->setAuthorReferrer($sanitized);
}
}
}
class SanitizerForClassB extends AbstractSanitizer
{
/* This class must provide an implementation for how ClassB will
* handle the sanitizeAuthor() method.
*/
public function sanitizeAuthor()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the for ClassB
$sanitized = new stdClass();
$sanitized->author = $this->handler->getAuthorReferrer();
echo "Sanitize author -> ClassB makes author an object property. \n";
$this->handler->setAuthorReferrer($sanitized);
}
}
}
class ClassA implements QueryArgumentsInterface
{
private $handler;
private $sanitizer;
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
{
$this->handler = $handler;
$this->sanitizer = $sanitizer;
$this->sanitizer->setHandler($this->handler);
}
public function queryArguments() // Will be used in the controller class ClassC
{
$queryArgs = null;
if($this->handler->hasAuthorReferrerValue())
{
$this->sanitizer->sanitizeAuthor();
$queryArgs = $this->handler->getAuthorReferrer();
}
if($this->handler->hasDateReferrerValue())
{
$this->sanitizer->sanitizeDate();
$queryArgs = $this->handler->getDateReferrer();
}
if($this->handler->hasSearchReferrerValue())
{
$this->sanitizer->sanitizeSearch();
$queryArgs = $this->handler->getSearchReferrer();
}
if($this->handler->hasTaxReferrerValue())
{
$this->sanitizer->sanitizeTaxonomy();
$queryArgs = $this->handler->getTaxReferrer();
}
return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
class ClassB implements QueryVarsInterface
{
private $handler;
private $sanitizer;
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
{
$this->handler = $handler;
$this->sanitizer = $sanitizer;
$this->sanitizer->setHandler($this->handler);
}
public function queryVars() // Will be used in the controller class ClassC
{
$queryVars = null;
if($this->handler->hasAuthorReferrerValue())
{
$this->sanitizer->sanitizeAuthor();
$queryVars = $this->handler->getAuthorReferrer();
}
if($this->handler->hasDateReferrerValue())
{
$this->sanitizer->sanitizeDate();
$queryVars = $this->handler->getDateReferrer();
}
if($this->handler->hasSearchReferrerValue())
{
$this->sanitizer->sanitizeSearch();
$queryVars = $this->handler->getSearchReferrer();
}
if($this->handler->hasTaxReferrerValue())
{
$this->sanitizer->sanitizeTaxonomy();
$queryVars = $this->handler->getTaxReferrer();
}
return $queryVars; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
class Controller
{
public function __construct() {}
public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass)
{
var_dump($argsClass->queryArguments());
var_dump($varsClass->queryVars());
}
}
/*
* TEST DRIVE
*/
//Create a controller that will use the classes
$controller = new Controller();
//Now make use of your new shiny handlers and sanitizers
$controller->doStuff(
new ClassA(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassB())
);