Tại sao lớp học của tôi tệ hơn thứ bậc của các lớp trong cuốn sách (người mới bắt đầu OOP)?


11

Tôi đang đọc các đối tượng, mô hình và thực hành PHP . Tác giả đang cố gắng mô hình hóa một bài học trong một trường đại học. Mục tiêu là để xuất ra loại bài học (bài giảng hoặc hội thảo), và chi phí cho bài học tùy thuộc vào việc đó là một bài học giá hàng giờ hay giá cố định. Vì vậy, đầu ra nên được

Lesson charge 20. Charge type: hourly rate. Lesson type: seminar.
Lesson charge 30. Charge type: fixed rate. Lesson type: lecture.

khi đầu vào như sau:

$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');

Tôi đã viết điều này:

class Lesson {
    private $chargeType;
    private $duration;
    private $lessonType;

    public function __construct($chargeType, $duration, $lessonType) {
        $this->chargeType = $chargeType;
        $this->duration = $duration;
        $this->lessonType = $lessonType;
    }

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

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

    public function cost() {
        if($this->chargeType == 'fixed rate') {
            return "30";
        } else {
            return $this->duration * 5;
        }
    }
}

$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');

foreach($lessons as $lesson) {
    print "Lesson charge {$lesson->cost()}.";
    print " Charge type: {$lesson->getChargeType()}.";
    print " Lesson type: {$lesson->getLessonType()}.";
    print "<br />";
}

Nhưng theo cuốn sách, tôi đã sai (tôi khá chắc chắn rằng tôi cũng vậy). Thay vào đó, tác giả đã đưa ra một hệ thống phân cấp lớn của các lớp làm giải pháp. Trong một chương trước, tác giả đã tuyên bố "bốn biển chỉ dẫn" sau đây là thời điểm tôi nên xem xét thay đổi cấu trúc lớp học của mình:

  • Sao chép mã
  • Lớp người biết quá nhiều về bối cảnh của nó
  • Các jack of all trades - lớp mà cố gắng làm nhiều điều
  • Câu điều kiện

Vấn đề duy nhất tôi có thể thấy là các tuyên bố có điều kiện, và điều đó cũng theo một cách mơ hồ - vậy tại sao lại cấu trúc lại điều này? Những vấn đề nào bạn nghĩ có thể phát sinh trong tương lai mà tôi không lường trước được?

Cập nhật : Tôi quên đề cập - đây là cấu trúc lớp mà tác giả đã cung cấp dưới dạng giải pháp - mẫu chiến lược :

Mô hình chiến lược


29
Đừng học lập trình hướng đối tượng từ một cuốn sách PHP.
giorgiosironi

4
@giorgiosironi Tôi đã từ bỏ việc đánh giá các ngôn ngữ. Tôi sử dụng PHP hàng ngày và vì vậy đây là cách nhanh nhất để tôi học các khái niệm mới trong đó. Luôn luôn có những người ghét một ngôn ngữ (Java: slidesha.re/91C4pX ) - thừa nhận thật khó để tìm thấy những kẻ thù ghét Java hơn dissers PHP, nhưng vẫn còn. Có thể có vấn đề với OO của PHP, nhưng hiện tại tôi không thể dành thời gian để học cú pháp Java, "cách Java" OOP. Bên cạnh đó, lập trình máy tính để bàn là xa lạ với tôi. Tôi là một người hoàn chỉnh web. Nhưng trước khi bạn có thể truy cập vào JSP, bạn bị cáo buộc cần phải biết Java trên máy tính để bàn (Không có trích dẫn, tôi có sai không?)
Aditya MP

Sử dụng các hằng số cho "tỷ lệ cố định" / "tỷ lệ hàng giờ" và "hội thảo" / "bài giảng" thay vì chuỗi.
Tom Marthenal

Câu trả lời:


19

Thật buồn cười là cuốn sách không nêu rõ, nhưng lý do tại sao nó ủng hộ một hệ thống phân cấp lớp hơn nếu các câu lệnh trong cùng một lớp có thể là nguyên tắc Mở / Đóng Quy tắc thiết kế phần mềm được biết đến rộng rãi này nói rằng một lớp nên được đóng lại sửa đổi nhưng mở để mở rộng.

Trong ví dụ của bạn, việc thêm một loại bài học mới có nghĩa là thay đổi mã nguồn của lớp Bài học, làm cho nó dễ bị hỏng và hồi quy. Nếu bạn có một lớp cơ sở và một đạo hàm cho mỗi loại bài học, thay vào đó bạn chỉ cần thêm một lớp con khác, thường được coi là sạch hơn.

Bạn có thể đặt quy tắc đó trong phần "Tuyên bố có điều kiện" nếu bạn muốn, tuy nhiên tôi cho rằng "biển chỉ dẫn" là một chút mơ hồ. Nếu các tuyên bố thường chỉ là mùi mã, triệu chứng. Họ có thể kết quả từ một loạt các quyết định thiết kế xấu.


3
Nó sẽ là một ý tưởng tốt hơn nhiều để sử dụng một phần mở rộng chức năng hơn là kế thừa.
DeadMG

6
Chắc chắn có những cách tiếp cận khác / tốt hơn cho vấn đề. Tuy nhiên, điều này rõ ràng là một cuốn sách về lập trình hướng đối tượng và nguyên tắc mở / đóng chỉ đánh tôi như là những cách cổ điển tiếp cận rằng loại tiến thoái lưỡng nan thiết kế trong OO.
guillaume31

Làm thế nào về cách tốt nhất để tiếp cận vấn đề ? Không có điểm nào dạy một giải pháp kém hơn chỉ vì đó là OO.
DeadMG

16

Tôi đoán rằng những gì cuốn sách nhắm đến giảng dạy là để tránh những điều như:

public function cost() {
    if($this->chargeType == 'fixed rate') {
        return "30";
    } else {
        return $this->duration * 5;
    }
}

Giải thích của tôi về cuốn sách là bạn nên có ba lớp:

  • Tóm tắt
  • HourlyRateLesson
  • FixedRateLesson

Sau đó, bạn nên thực hiện lại costchức năng trên cả hai lớp con.

Ý kiến ​​cá nhân của tôi

đối với những trường hợp đơn giản như thế: đừng. Thật kỳ lạ khi cuốn sách của bạn ủng hộ hệ thống phân cấp lớp sâu hơn để tránh các câu điều kiện đơn giản. Hơn nữa, với thông số kỹ thuật đầu vào của bạn, Lessonsẽ phải là một nhà máy với công văn là một tuyên bố có điều kiện. Vì vậy, với giải pháp cuốn sách bạn sẽ có:

  • 3 lớp thay vì 1
  • 1 cấp thừa kế thay vì 0
  • cùng số báo cáo điều kiện

Điều đó phức tạp hơn mà không có thêm lợi ích.


8
Trong trường hợp này , vâng, nó quá phức tạp. Nhưng trong một hệ thống lớn hơn, bạn sẽ được bổ sung thêm nhiều bài học, như SemesterLesson, MasterOneWeekLessonvv Một lớp rễ trừu tượng, hoặc tốt hơn là một giao diện, chắc chắn là con đường để đi sau đó. Nhưng khi bạn chỉ có hai trường hợp, tùy theo ý của tác giả.
Michael K

5
Tôi đồng ý với bạn nói chung. Tuy nhiên, các ví dụ giáo dục thường bị chiếm đoạt và kiến ​​trúc quá mức để minh họa một điểm. Bạn bỏ qua những cân nhắc khác trong một lúc để không nhầm lẫn vấn đề trong tay, và giới thiệu những cân nhắc đó sau.
Karl Bielefeldt

+1 phân tích kỹ lưỡng đẹp. Nhận xét, "Điều đó phức tạp hơn không có lợi ích gia tăng" minh họa cho khái niệm 'chi phí cơ hội' trong lập trình một cách hoàn hảo. Lý thuyết! = Thực tiễn.
Evan Plaice

7

Ví dụ trong cuốn sách khá lạ. Từ câu hỏi của bạn, tuyên bố ban đầu là:

Mục tiêu là đưa ra Loại Bài học (Bài giảng hoặc Hội thảo) và các khoản phí cho bài học tùy thuộc vào việc đó là bài học giá hàng giờ hay giá cố định.

trong bối cảnh của một chương về OOP, có nghĩa là tác giả có thể muốn bạn có cấu trúc như sau:

+ abstract Lesson
    - Lecture inherits Lesson
    - Seminar inherits Lesson

+ abstract Charge
    - HourlyCharge inherits Charge
    - FixedCharge inherits Charge

Nhưng sau đó đến đầu vào bạn trích dẫn:

$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');

tức là một cái gì đó được gọi là mã được gõ theo chuỗi , và thành thật mà nói, trong bối cảnh này khi người đọc có ý định học OOP, thật kinh khủng.

Điều này cũng có nghĩa là nếu tôi đúng về ý định của tác giả cuốn sách (nghĩa là tạo ra các lớp trừu tượng và kế thừa mà tôi đã liệt kê ở trên), điều này sẽ dẫn đến mã trùng lặp và khá khó đọc và xấu xí:

const string $HourlyRate = 'hourly rate';
const string $FixedRate = 'fixed rate';

// [...]

Charge $charge;
switch ($chargeType)
{
    case $HourlyRate:
        $charge = new HourlyCharge();
        break;

    case $FixedRate:
        $charge = new FixedCharge();
        break;

    default:
        throw new ParserException('The value of chargeType is unexpected.');
}

// Imagine the similar code for lecture/seminar, and the similar code for both on output.

Đối với "biển chỉ dẫn":

  • Sao chép mã

    Mã của bạn không có sự trùng lặp. Các mã tác giả mong đợi bạn viết.

  • Lớp người biết quá nhiều về bối cảnh của mình

    Lớp của bạn chỉ truyền các chuỗi từ đầu vào đến đầu ra và không biết gì cả. Mặt khác, mã dự kiến ​​có thể bị chỉ trích vì biết quá nhiều.

  • The Jack of All Trades - Các lớp học cố gắng làm nhiều việc

    Một lần nữa, bạn chỉ cần truyền chuỗi, không có gì hơn.

  • Câu điều kiện

    Có một tuyên bố có điều kiện trong mã của bạn. Nó dễ đọc và dễ hiểu.


Có thể, trên thực tế, tác giả mong đợi bạn viết một mã được tái cấu trúc nhiều hơn so với mã có sáu lớp ở trên. Ví dụ: bạn có thể sử dụng mô hình nhà máy cho các bài học và chi phí, v.v.

+ abstract Lesson
    - Lecture inherits Lesson
    - Seminar inherits Lesson

+ abstract Charge
    - HourlyCharge inherits Charge
    - FixedCharge inherits Charge

+ LessonFactory

+ ChargeFactory

+ Parser // Uses factories to transform the input into `Lesson`.

Trong trường hợp này, bạn sẽ kết thúc với chín lớp, đây sẽ là một ví dụ hoàn hảo về kiến trúc quá mức .

Thay vào đó, bạn đã kết thúc với một mã sạch, dễ hiểu, ngắn hơn mười lần.


Wow, dự đoán của bạn là chính xác! Nhìn vào hình ảnh tôi đã tải lên - tác giả đề xuất chính xác những điều tương tự.
MP Aditya

Tôi rất muốn chấp nhận câu trả lời này, nhưng tôi cũng thích câu trả lời của @ ian31 :( Ồ, tôi phải làm gì!
Aditya MP

5

Trước hết, bạn có thể thay đổi "số ma thuật" như hàm 30 và 5 trong hàm cost () thành các biến có ý nghĩa hơn :)

Và thành thật mà nói, tôi nghĩ rằng bạn không nên lo lắng về điều này. Khi bạn cần thay đổi lớp thì bạn sẽ thay đổi nó. Cố gắng tạo giá trị trước sau đó chuyển sang tái cấu trúc.

Và tại sao bạn nghĩ rằng bạn sai? Không phải lớp học này "đủ tốt" cho bạn sao? Tại sao bạn lo lắng về một số cuốn sách;)


1
Cảm ơn đã trả lời! Thật vậy, tái cấu trúc sẽ đến sau. Tuy nhiên, tôi đã viết mã này để học, cố gắng giải quyết vấn đề được trình bày trong cuốn sách theo cách của riêng tôi và xem tôi đã sai như thế nào ...
Aditya MP

2
Nhưng bạn sẽ biết điều này sau. Khi lớp này sẽ được sử dụng trong các phần khác của hệ thống và bạn sẽ phải thêm một cái gì đó mới. Không có giải pháp "viên đạn bạc" :) Một cái gì đó có thể tốt hay xấu, nó phụ thuộc vào hệ thống, yêu cầu, v.v. Trong khi học OOP bạn phải thất bại rất nhiều: P Sau đó, theo thời gian, bạn sẽ nhận thấy một số vấn đề và mô hình phổ biến. Tbh ví dụ này là quá đơn giản để thực hiện một số lời khuyên. Ohh tôi thực sự đề xuất Bài đăng này từ Joel :) Các phi hành gia kiến ​​trúc tiếp quản joelonsoftware.com/items/2008/05/01.html
Michal Franc

@adityamenon Sau đó, câu trả lời của bạn là "tái cấu trúc sẽ đến sau". Chỉ thêm các lớp khác một khi chúng cần để làm cho mã đơn giản hơn - thông thường, đó là khi trường hợp sử dụng tương tự thứ 3 xuất hiện. Xem câu trả lời của Simon.
Izkata

3

Hoàn toàn không cần bất cứ điều gì để thực hiện bất kỳ lớp bổ sung nào. Bạn có một chút MAGIC_NUMBERvấn đề trong cost()chức năng của mình , nhưng đó là vấn đề. Bất cứ điều gì khác là một kỹ thuật quá lớn. Tuy nhiên, thật không may là rất phổ biến khi đưa ra lời khuyên rất tệ - chẳng hạn như Circle thừa hưởng Hình dạng. Nó chắc chắn không hiệu quả trong bất kỳ cách nào để lấy được một lớp cho sự kế thừa đơn giản của một hàm. Bạn có thể sử dụng một cách tiếp cận chức năng để tùy chỉnh nó thay thế.


1
Hay đấy. Bạn có thể giải thích làm thế nào bạn sẽ làm điều đó với một ví dụ?
guillaume31

+1, tôi đồng ý, không có lý do gì để kỹ sư quá mức / làm phức tạp một chút chức năng nhỏ như vậy.
GrandmasterB

@ ian31: Làm gì, cụ thể?
DeadMG

@DeadMG "sử dụng phương pháp tiếp cận chức năng", "sử dụng tiện ích mở rộng chức năng thay vì kế thừa"
guillaume31
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.