ประกาศบริการสาธารณะ:
ฉันต้องการระบุสำหรับบันทึกที่ฉันเชื่อว่าลักษณะเป็นกลิ่นรหัสเกือบทุกครั้งและควรหลีกเลี่ยงในความโปรดปรานขององค์ประกอบ เป็นความเห็นของฉันว่าการสืบทอดเพียงครั้งเดียวมักถูกทารุณกรรมจนถึงจุดที่เป็นรูปแบบการต่อต้านและการสืบทอดหลายอย่างเท่านั้นที่รวมปัญหานี้ คุณจะได้รับบริการที่ดีขึ้นในหลาย ๆ กรณีโดยการจัดองค์ประกอบการสืบทอด (ไม่ว่าจะเป็นรายการเดียวหรือหลายรายการ) หากคุณยังคงสนใจในลักษณะและความสัมพันธ์ของพวกเขากับอินเทอร์เฟซอ่านต่อ ...
เริ่มต้นด้วยการพูดสิ่งนี้:
การเขียนโปรแกรมเชิงวัตถุ (OOP) อาจเป็นกระบวนทัศน์ที่ยากต่อการเข้าใจ เพียงเพราะคุณกำลังใช้คลาสไม่ได้หมายความว่ารหัสของคุณคือ Object-Oriented (OO)
ในการเขียนรหัส OO คุณต้องเข้าใจว่า OOP นั้นเกี่ยวกับความสามารถของวัตถุของคุณจริงๆ คุณต้องคิดถึงคลาสในแง่ของสิ่งที่พวกเขาสามารถทำได้แทนที่จะเป็นสิ่งที่พวกเขาทำจริงๆ สิ่งนี้ตรงกันข้ามกับการเขียนโปรแกรมตามขั้นตอนแบบดั้งเดิมโดยมุ่งเน้นที่การสร้างโค้ด "ทำบางสิ่งบางอย่าง"
ถ้ารหัส OOP เกี่ยวกับการวางแผนและออกแบบส่วนต่อประสานเป็นพิมพ์เขียวและวัตถุเป็นบ้านที่สร้างขึ้นอย่างสมบูรณ์ ในขณะเดียวกันลักษณะเป็นเพียงวิธีที่จะช่วยสร้างบ้านที่วางโดยพิมพ์เขียว (อินเทอร์เฟซ)
อินเตอร์เฟซ
ดังนั้นทำไมเราจึงควรใช้อินเตอร์เฟส ค่อนข้างง่ายอินเตอร์เฟสทำให้โค้ดของเราเปราะน้อยลง หากคุณสงสัยในข้อความนี้ให้ถามผู้ที่ถูกบังคับให้รักษารหัสดั้งเดิมที่ไม่ได้เขียนไว้กับส่วนต่อประสาน
อินเทอร์เฟซเป็นสัญญาระหว่างโปรแกรมเมอร์และรหัสของเขา / เธอ อินเทอร์เฟซกล่าวว่า "ตราบใดที่คุณเล่นตามกฎของฉันคุณสามารถใช้ฉันได้ตามที่คุณต้องการและฉันสัญญาว่าฉันจะไม่ทำลายรหัสอื่นของคุณ"
เพื่อเป็นตัวอย่างให้พิจารณาสถานการณ์จริง (ไม่มีรถยนต์หรือวิดเจ็ต):
คุณต้องการใช้ระบบแคชสำหรับเว็บแอปพลิเคชันเพื่อลดภาระของเซิร์ฟเวอร์
คุณเริ่มต้นด้วยการเขียนชั้นเรียนเพื่อแคตอบคำขอโดยใช้ APC:
class ApcCacher
{
public function fetch($key) {
return apc_fetch($key);
}
public function store($key, $data) {
return apc_store($key, $data);
}
public function delete($key) {
return apc_delete($key);
}
}
จากนั้นในวัตถุตอบกลับ HTTP ของคุณคุณตรวจสอบแคชการเข้าชมก่อนที่จะทำงานทั้งหมดเพื่อสร้างการตอบสนองที่แท้จริง:
class Controller
{
protected $req;
protected $resp;
protected $cacher;
public function __construct(Request $req, Response $resp, ApcCacher $cacher=NULL) {
$this->req = $req;
$this->resp = $resp;
$this->cacher = $cacher;
$this->buildResponse();
}
public function buildResponse() {
if (NULL !== $this->cacher && $response = $this->cacher->fetch($this->req->uri()) {
$this->resp = $response;
} else {
// Build the response manually
}
}
public function getResponse() {
return $this->resp;
}
}
วิธีนี้ใช้งานได้ดี แต่ไม่กี่สัปดาห์ต่อมาคุณตัดสินใจว่าคุณต้องการใช้ระบบแคชแบบไฟล์แทน APC ตอนนี้คุณต้องเปลี่ยนรหัสคอนโทรลเลอร์ของคุณเพราะคุณได้ตั้งโปรแกรมคอนโทรลเลอร์ให้ทำงานกับฟังก์ชันการทำงานของApcCacher
คลาสแทนที่จะเป็นอินเทอร์เฟซที่แสดงความสามารถของApcCacher
คลาส สมมติว่าแทนที่จะเป็นข้างต้นคุณได้ให้Controller
ชั้นเรียนพึ่งพาCacherInterface
แทนที่จะเป็นรูปธรรมApcCacher
ดังนี้:
// Your controller's constructor using the interface as a dependency
public function __construct(Request $req, Response $resp, CacherInterface $cacher=NULL)
หากต้องการไปพร้อมกับที่คุณกำหนดอินเทอร์เฟซของคุณเช่น:
interface CacherInterface
{
public function fetch($key);
public function store($key, $data);
public function delete($key);
}
ในทางกลับกันคุณจะมีทั้งคลาสของคุณApcCacher
และFileCacher
คลาสใหม่ของคุณใช้CacherInterface
และคุณตั้งโปรแกรมController
คลาสของคุณเพื่อใช้ความสามารถที่ต้องการโดยอินเทอร์เฟซ
ตัวอย่างนี้ (หวังว่า) แสดงให้เห็นว่าการเขียนโปรแกรมไปยังอินเทอร์เฟซช่วยให้คุณเปลี่ยนการใช้งานภายในห้องเรียนของคุณได้อย่างไรโดยไม่ต้องกังวลว่าการเปลี่ยนแปลงจะทำให้รหัสอื่นของคุณเสียหาย
ลักษณะ
ในทางกลับกันคุณสมบัติเป็นเพียงวิธีการใช้รหัสอีกครั้ง การเชื่อมต่อไม่ควรถูกมองว่าเป็นทางเลือกที่ไม่เหมือนกัน ในความเป็นจริงการสร้างลักษณะที่ตอบสนองความสามารถที่จำเป็นโดยอินเตอร์เฟซเป็นกรณีการใช้งานที่เหมาะ
คุณควรใช้ลักษณะเฉพาะเมื่อมีหลายคลาสที่ใช้ฟังก์ชันเดียวกันร่วมกัน (อาจเป็นไปได้โดยอินเทอร์เฟซเดียวกัน) ไม่มีความหมายในการใช้คุณลักษณะเพื่อให้มีฟังก์ชั่นสำหรับคลาสเดียวนั่นคือทำให้งงงวยว่าสิ่งใดที่คลาสทำและการออกแบบที่ดีกว่าจะย้ายการทำงานของคุณลักษณะไปยังคลาสที่เกี่ยวข้อง
พิจารณาการใช้คุณลักษณะต่อไปนี้:
interface Person
{
public function greet();
public function eat($food);
}
trait EatingTrait
{
public function eat($food)
{
$this->putInMouth($food);
}
private function putInMouth($food)
{
// Digest delicious food
}
}
class NicePerson implements Person
{
use EatingTrait;
public function greet()
{
echo 'Good day, good sir!';
}
}
class MeanPerson implements Person
{
use EatingTrait;
public function greet()
{
echo 'Your mother was a hamster!';
}
}
ตัวอย่างที่เป็นรูปธรรมมากขึ้น: ลองจินตนาการว่าทั้งคุณFileCacher
และของคุณApcCacher
จากการพูดคุยในส่วนต่อประสานใช้วิธีการเดียวกันเพื่อกำหนดว่ารายการแคชเก่าและควรถูกลบหรือไม่ (เห็นได้ชัดว่านี่ไม่ใช่กรณีในชีวิตจริง คุณสามารถเขียนคุณลักษณะและอนุญาตให้ทั้งสองคลาสใช้สำหรับข้อกำหนดอินเทอร์เฟซทั่วไป
หนึ่งคำสุดท้ายที่ควรระวัง: ระวังอย่าไปลงเรือด้วยคุณลักษณะ บ่อยครั้งที่มีการใช้ลักษณะเป็นไม้ยันรักแร้สำหรับการออกแบบที่ไม่ดีเมื่อการใช้งานระดับที่ไม่ซ้ำกันจะเพียงพอ คุณควร จำกัด คุณสมบัติในการปฏิบัติตามข้อกำหนดของอินเทอร์เฟซสำหรับการออกแบบรหัสที่ดีที่สุด