2008-09-18 34 views
90

Tôi đang tìm kiếm một cách tốt, sạch sẽ để đi xung quanh thực tế là PHP5 vẫn không hỗ trợ đa kế thừa. Dưới đây là hệ thống phân cấp lớp:Nhiều thừa kế trong PHP

nhắn
- TextMessage
-------- InvitationTextMessage
- EmailMessage
-------- InvitationEmailMessage

Hai loại Lời mời * các lớp học có nhiều điểm chung; Tôi rất muốn có một lớp cha mẹ chung, Lời mời, rằng cả hai đều sẽ kế thừa từ. Thật không may, họ cũng có rất nhiều điểm chung với tổ tiên hiện tại của họ ... TextMessage và EmailMessage. Mong muốn cổ điển cho đa di sản ở đây.

Cách tiếp cận trọng lượng nhẹ nhất để giải quyết vấn đề là gì?

Cảm ơn!

+4

Không có nhiều trường hợp trong đó thừa kế (hoặc thậm chí đa thừa kế) là chính đáng. Xem xét các nguyên tắc SOLID. Ưu tiên thành phần trên thừa kế. –

+2

@ OndřejMirtes những gì bạn có nghĩa là - "không nhiều trường hợp trong đó thừa kế là chính đáng."? – styler1972

+9

Ý tôi là - thừa kế mang lại nhiều vấn đề hơn lợi ích (xem nguyên tắc thay thế Liskov). Bạn có thể giải quyết hầu hết mọi thứ với bố cục và tiết kiệm rất nhiều đau đầu. Thừa kế cũng tĩnh - điều đó có nghĩa là bạn không thể thay đổi nội dung được viết trong mã. Nhưng sự tích hợp có thể được sử dụng trong thời gian chạy và bạn có thể chọn triển khai động - e. g. tái sử dụng cùng một lớp với các cơ chế lưu trữ khác nhau. –

Trả lời

139

Alex, hầu hết các lần bạn cần đa thừa kế là tín hiệu cấu trúc đối tượng của bạn có phần không chính xác. Trong tình huống bạn vạch ra tôi thấy bạn có trách nhiệm lớp học đơn giản là quá rộng. Nếu Thư là một phần của mô hình kinh doanh ứng dụng, thì nó không nên quan tâm đến kết quả hiển thị. Thay vào đó, bạn có thể phân chia trách nhiệm và sử dụng MessageDispatcher gửi thông báo bằng cách sử dụng văn bản hoặc phần phụ trợ html. Tôi không biết mã của bạn, nhưng hãy để tôi mô phỏng nó theo cách này:

$m = new Message(); 
$m->type = 'text/html'; 
$m->from = 'John Doe <[email protected]>'; 
$m->to = 'Random Hacker <[email protected]>'; 
$m->subject = 'Invitation email'; 
$m->importBody('invitation.html'); 

$d = new MessageDispatcher(); 
$d->dispatch($m); 

Bằng cách này bạn có thể thêm một số chuyên môn hoá để nhắn lớp:

$htmlIM = new InvitationHTMLMessage(); // html type, subject and body configuration in constructor 
$textIM = new InvitationTextMessage(); // text type, subject and body configuration in constructor 

$d = new MessageDispatcher(); 
$d->dispatch($htmlIM); 
$d->dispatch($textIM); 

Lưu ý rằng MessageDispatcher sẽ đưa ra quyết định liệu có nên gửi dưới dạng HTML hoặc văn bản thuần túy tùy thuộc vào thuộc tính type trong đối tượng Thư được chuyển.

// in MessageDispatcher class 
public function dispatch(Message $m) { 
    if ($m->type == 'text/plain') { 
     $this->sendAsText($m); 
    } elseif ($m->type == 'text/html') { 
     $this->sendAsHTML($m); 
    } else { 
     throw new Exception("MIME type {$m->type} not supported"); 
    } 
} 

Tóm lại, trách nhiệm được phân chia giữa hai lớp. Cấu hình thư được thực hiện trong lớp InvitationHTMLMessage/InvitationTextMessage và thuật toán gửi được ủy quyền cho người điều phối.Đây được gọi là Mẫu chiến lược, bạn có thể đọc thêm trên đó here.

+12

Câu trả lời tuyệt vời, cảm ơn bạn! Tôi đã học được điều gì đó ngày hôm nay! –

+22

...Tôi biết điều này là một chút cũ (Tôi đã tìm kiếm để xem nếu PHP có MI ... chỉ cho tò mò) Tôi không nghĩ rằng đây là một ví dụ tốt về mô hình chiến lược. Mẫu chiến lược được thiết kế để bạn có thể triển khai "chiến lược" mới bất kỳ lúc nào. Việc triển khai bạn đã cung cấp không có khả năng như vậy. Thay vào đó, thông báo sẽ có chức năng "gửi" gọi là MessageDispatcher-> dispatch() (Dispatcher hoặc param hoặc thành viên var), và các lớp mới HTMLDispatcher & TextDispatcher sẽ thực hiện "dispatch" theo cách tương ứng của chúng (điều này cho phép các Dispatchers khác để làm công việc khác) –

+5

Tôi thực sự muốn SO sẽ không gây rối với khoảng trống của tôi ... –

2

Có vẻ như số decorator pattern có thể phù hợp nhưng khó nói mà không có thêm chi tiết.

6

Khuôn khổ Symfony có số mixin plugin for this, bạn có thể muốn kiểm tra - thậm chí chỉ dành cho các ý tưởng, nếu không sử dụng.

Câu trả lời "mẫu thiết kế" là trừu tượng hóa chức năng được chia sẻ thành một thành phần riêng biệt và soạn khi chạy. Hãy suy nghĩ về một cách trừu tượng hóa chức năng Lời mời như một lớp được liên kết với các lớp Tin nhắn của bạn theo một cách nào đó không phải là thừa kế.

+0

liên kết symfony bị hỏng ... – m13r

0

Vấn đề tương tự như Java. Hãy thử sử dụng các giao diện có chức năng trừu tượng để giải quyết vấn đề đó

13

Có thể bạn có thể thay thế quan hệ 'is-a' bằng quan hệ 'có-a'? Lời mời có thể có một tin nhắn, nhưng nó không nhất thiết cần phải có thông báo 'is-a'. Lời mời f.e. có thể được xác nhận, điều này không phù hợp với mô hình Tin nhắn.

Tìm kiếm 'thành phần so với thừa kế' nếu bạn cần biết thêm về điều đó.

0

PHP hỗ trợ giao diện. Đây có thể là một cược tốt, tùy thuộc vào trường hợp sử dụng của bạn.

+4

Giao diện không cho phép triển khai chức năng cụ thể, vì vậy chúng không hữu ích ở đây. –

+0

Giao diện hỗ trợ nhiều Thừa kế, không giống như các lớp. –

1

Tôi có một vài câu hỏi để hỏi để làm rõ những gì bạn đang làm:

1) Liệu đối tượng thông điệp của bạn chỉ chứa một thông điệp ví dụ cơ thể, người nhận, thời gian biểu? 2) Bạn định làm gì với đối tượng Lời mời? Liệu nó có cần phải được xử lý đặc biệt so với một EmailMessage không? 3) Nếu có gì đặc biệt về nó? 4) Nếu đó là trường hợp, tại sao các loại tin nhắn cần xử lý khác nhau cho một lời mời? 5) Nếu bạn muốn gửi tin nhắn chào mừng hoặc tin nhắn OK thì sao? Họ cũng là những đối tượng mới?

Có vẻ như bạn đang cố gắng kết hợp quá nhiều chức năng vào một tập hợp các đối tượng chỉ nên quan tâm đến việc giữ nội dung thư - chứ không phải cách xử lý. Với tôi, bạn thấy đấy, không có sự khác biệt giữa lời mời hoặc tin nhắn chuẩn. Nếu lời mời yêu cầu xử lý đặc biệt, thì điều đó có nghĩa là logic ứng dụng và không phải là loại thông báo.

Ví dụ: hệ thống tôi đã tạo có đối tượng tin nhắn cơ bản dùng chung được mở rộng thành SMS, Email và các loại tin nhắn khác. Tuy nhiên: chúng không được mở rộng thêm nữa - một thông báo mời chỉ đơn giản là văn bản được xác định trước sẽ được gửi qua một tin nhắn kiểu Email. Một ứng dụng Lời mời cụ thể sẽ liên quan đến việc xác thực và các yêu cầu khác cho một lời mời. Sau khi tất cả, tất cả các bạn muốn làm là gửi tin nhắn X cho người nhận Y mà phải là một hệ thống rời rạc riêng của mình.

3

Đây là cả một câu hỏi và một giải pháp ....

gì về huyền diệu _ call(), _GET(), __set() phương pháp? Tôi chưa thử nghiệm giải pháp này nhưng nếu bạn thực hiện một lớp multiInherit. Một biến được bảo vệ trong một lớp con có thể chứa một mảng các lớp để kế thừa. Hàm khởi tạo trong lớp đa giao diện có thể tạo ra các cá thể của mỗi lớp đang được kế thừa và liên kết chúng với một thuộc tính riêng, nói _ext. Phương thức __call() có thể sử dụng hàm method_exists() trên mỗi lớp trong mảng _ext để định vị phương thức chính xác để gọi. __get() và __set có thể được sử dụng để định vị các thuộc tính bên trong, hoặc nếu chuyên gia của bạn có tham chiếu, bạn có thể làm cho các thuộc tính của lớp con và các lớp kế thừa được tham chiếu đến cùng một dữ liệu. Việc thừa kế nhiều đối tượng của bạn sẽ được minh bạch để mã hóa bằng cách sử dụng các đối tượng đó. Ngoài ra, các đối tượng bên trong có thể truy cập trực tiếp các đối tượng thừa kế nếu cần thiết miễn là mảng _ext được lập chỉ mục theo tên lớp. Tôi đã hình dung ra việc tạo ra siêu lớp này và chưa thực hiện nó như tôi cảm thấy rằng nếu nó hoạt động hơn nó có thể dẫn đến phát triển một số thói quen lập trình khác nhau xấu.

+0

Tôi nghĩ điều này là khả thi. Nó sẽ kết hợp chức năng của nhiều lớp, nhưng sẽ không thực sự kế thừa chúng (theo nghĩa 'instanceof') – user102008

+0

Và điều này chắc chắn sẽ không cho phép ghi đè ngay khi lớp bên trong thực hiện cuộc gọi đến tự ::

7

Nếu tôi có thể báo Phil trong this thread ...

PHP, như Java, không hỗ trợ đa kế thừa.

Đến với PHP 5.4 sẽ là traits cố gắng cung cấp giải pháp cho sự cố này.

Trong thời gian chờ đợi, bạn nên suy nghĩ lại về thiết kế lớp học của mình. Bạn có thể triển khai nhiều giao diện nếu bạn sau một API mở rộng đến các lớp học của bạn.

Và Chris ....

PHP không thực sự hỗ trợ đa kế thừa, nhưng có một số cách (hơi lộn xộn) để triển khai. Kiểm tra URL này đối với một số ví dụ:

http://www.jasny.net/articles/how-i-php-multiple-inheritance/

tưởng cả hai đều có liên kết hữu ích. Không thể chờ đợi để thử các đặc điểm hoặc có thể một số mixin ...

-1

Lớp học Lời mời ngay bên dưới lớp Tin nhắn như thế nào?

nên hệ thống phân cấp đi:

nhắn
--- Lời mời
------ TextMessage
------ EmailMessage

Và trong lớp mời, thêm các chức năng đã có trong InvitationTextMessage và InvitationEmailMessage.

Tôi biết rằng Lời mời không thực sự là một loại Tin nhắn, đó là một chức năng của Tin nhắn. Vì vậy, tôi không chắc chắn nếu điều này là tốt OO thiết kế hay không.

3

Tôi đang sử dụng các đặc điểm trong PHP 5.4 làm cách để giải quyết vấn đề này. http://php.net/manual/en/language.oop5.traits.php

Điều này cho phép thừa kế cổ điển với mở rộng, nhưng cũng cho phép đặt chức năng và đặc tính chung vào một 'đặc điểm'. Khi hướng dẫn sử dụng cho biết:

Đặc điểm là một cơ chế để sử dụng lại mã bằng các ngôn ngữ kế thừa đơn lẻ như PHP. Một Trait nhằm giảm bớt một số hạn chế của thừa kế đơn lẻ bằng cách cho phép một nhà phát triển sử dụng lại các phương thức một cách tự do trong một số lớp độc lập sống trong các phân cấp lớp khác nhau.