Lập trình hướng đối tượng: getters / setters hoặc tên logic


12

Tôi hiện đang nghĩ về một giao diện cho một lớp tôi đang viết. Lớp này chứa các kiểu cho một ký tự, ví dụ: cho dù ký tự đó được in đậm, in nghiêng, gạch chân, v.v. Tôi đã tranh luận với chính mình trong hai ngày liệu tôi có nên sử dụng getters / setters hoặc tên logic cho các phương thức thay đổi giá trị thành những phong cách này. Mặc dù tôi có xu hướng thích các tên logic, nhưng điều đó có nghĩa là viết mã không hiệu quả và không logic. Tôi sẽ cho bạn một ví dụ.

Tôi đã có một lớp CharacterStylesmà có các biến thành viên bold, italic, underline(và một số người khác, nhưng tôi sẽ rời chúng ra để giữ cho nó đơn giản). Cách dễ nhất để cho phép các phần khác của chương trình truy cập vào các biến này, là viết các phương thức getter / setter, để bạn có thể làm styles.setBold(true)styles.setItalic(false).

Nhưng tôi không thích điều này. Không chỉ bởi vì nhiều người nói rằng getters / setters phá vỡ sự đóng gói (nó thực sự tệ đến vậy sao?), Mà chủ yếu là vì nó có vẻ không hợp lý với tôi. Tôi hy vọng sẽ tạo kiểu cho một nhân vật thông qua một phương thức, styles.format("bold", true)hoặc một cái gì đó tương tự, nhưng không phải thông qua tất cả các phương thức này.

Dù vậy vẫn có một vấn đề. Vì bạn không thể truy cập biến thành viên đối tượng theo nội dung của chuỗi trong C ++, tôi sẽ phải viết một thùng chứa if-statement / switch lớn cho tất cả các kiểu hoặc tôi sẽ phải lưu trữ các kiểu trong một mảng kết hợp ( bản đồ).

Tôi không thể tìm ra cách tốt nhất là gì. Một khoảnh khắc tôi nghĩ rằng tôi nên viết getters / setters, và khoảnh khắc tiếp theo tôi nghiêng về phía khác. Câu hỏi của tôi là: bạn sẽ làm gì? Và tại sao bạn lại làm vậy?


Có phải lớp CharacterStyles thực sự làm bất cứ điều gì khác ngoài gói ba booleans không? Làm thế nào để nó được tiêu thụ?
Đánh dấu Canlas

Có, bởi vì CharacterStyles sẽ có thể kế thừa từ các Kiểu nhân vật khác. Điều này có nghĩa là nếu tôi có một Kiểu nhân vật boldđược đặt thành truevà các biến khác không được xác định, các phương thức getter cho các biến khác sẽ trả về giá trị của kiểu cha (được lưu trữ trong thuộc tính khác), trong khi getter cho thuộc boldtính sẽ trả về true. Có một số thứ khác như tên và cách nó cũng được hiển thị trong giao diện.
Ếch

Bạn có thể sử dụng một enum để xác định các tùy chọn kiểu khác nhau, sau đó sử dụng câu lệnh chuyển đổi. Btw, làm thế nào bạn xử lý không xác định?
Tyanna

@Tyanna: Thật vậy, tôi cũng đã nghĩ đến việc sử dụng enum, nhưng đó chỉ là một chi tiết nhỏ. Tôi đã dự định chỉ đặt các giá trị không xác định thành NULL. Đó không phải là cách tốt nhất sao?
Ếch

Câu trả lời:


6

Có, getters / setters thực hiện đóng gói phá vỡ - về cơ bản chúng chỉ là một lớp bổ sung giữa việc truy cập trực tiếp vào trường bên dưới. Bạn cũng có thể chỉ cần truy cập trực tiếp.

Bây giờ, nếu bạn muốn các phương thức phức tạp hơn để truy cập vào trường, điều đó hợp lệ, nhưng thay vì phơi bày trường, bạn cần phải nghĩ phương thức nào mà lớp sẽ cung cấp thay thế. I E. thay vì lớp Ngân hàng có giá trị Tiền được hiển thị bằng tài sản, bạn cần nghĩ loại truy cập nào mà đối tượng Ngân hàng nên cung cấp (thêm tiền, rút ​​tiền, lấy số dư) và thực hiện thay thế. Một thuộc tính trên biến Money chỉ khác về mặt cú pháp so với việc hiển thị trực tiếp biến Money.

DrDobbs có một bài viết nói nhiều hơn.

Đối với vấn đề của bạn, tôi có 2 phương thức setStyle và clearStyle (hoặc bất cứ điều gì) có thể liệt kê các kiểu có thể. Một câu lệnh chuyển đổi bên trong các phương thức này sau đó sẽ áp dụng các giá trị liên quan cho các biến lớp thích hợp. Bằng cách này, bạn có thể thay đổi biểu diễn bên trong của các kiểu thành một kiểu khác nếu sau đó bạn quyết định lưu trữ chúng dưới dạng một chuỗi (ví dụ để sử dụng trong HTML) - một cái gì đó sẽ yêu cầu tất cả người dùng của lớp của bạn cũng phải thay đổi nếu bạn sử dụng nhận / đặt thuộc tính.

Bạn vẫn có thể bật chuỗi nếu bạn muốn nhận các giá trị tùy ý, có câu lệnh if-then lớn (nếu có một vài trong số chúng) hoặc ánh xạ các giá trị chuỗi sang con trỏ phương thức bằng std :: mem_fun (hoặc std :: hàm ) vì vậy "bold" sẽ được lưu trữ trong khóa bản đồ với giá trị của nó là sts :: mem_fun thành phương thức đặt biến đậm thành true (nếu các chuỗi giống như tên biến thành viên, thì bạn cũng có thể sử dụng macro xâu chuỗi để giảm số lượng mã bạn cần viết)


Câu trả lời tuyệt vời! Đặc biệt là phần lưu trữ dữ liệu dưới dạng HTML đánh vào tôi như một lý do chính đáng để thực sự đi vào con đường này. Vì vậy, bạn sẽ đề nghị lưu trữ các phong cách khác nhau trong một enum và sau đó thiết lập các kiểu như thế styles.setStyle(BOLD, true)nào? Đúng không?
Ếch

có, chỉ có tôi có 2 phương thức - setStyle (BOLD) và Clearstyle (BOLD). quên tham số thứ 2, tôi chỉ thích nó như thế và sau đó bạn có thể quá tải ClearStyle để không có tham số nào để xóa tất cả các cờ kiểu.
gbjbaanb

Điều đó sẽ không hoạt động, bởi vì một số loại (như cỡ chữ) cần một tham số. Nhưng vẫn cảm ơn vì câu trả lời!
Ếch

Tôi đã cố gắng thực hiện điều này, nhưng có một vấn đề tôi chưa xem xét: làm thế nào để bạn thực hiện styles.getStyle(BOLD)khi bạn không chỉ có các biến thành viên của kiểu boolean, mà còn cả kiểu số nguyên và chuỗi (ví dụ styles.getStyle(FONTSIZE):). Vì bạn không thể quá tải các loại trả về, làm thế nào để bạn lập trình này? Tôi biết bạn có thể sử dụng con trỏ void, nhưng điều đó nghe có vẻ như là một cách thực sự tồi tệ đối với tôi. Bạn có bất cứ gợi ý về cách làm điều này?
Ếch

bạn có thể trả về một liên minh, hoặc một cấu trúc, hoặc với tiêu chuẩn mới, bạn có thể quá tải theo kiểu trả về. Điều đó nói rằng, bạn đang cố gắng thực hiện một phương thức duy nhất để đặt kiểu, trong đó kiểu là cờ và họ phông chữ và kích thước? Có lẽ bạn đang cố gắng ép buộc sự trừu tượng quá nhiều trong trường hợp này.
gbjbaanb

11

Một ý tưởng bạn có thể không cân nhắc là Mẫu trang trí . Thay vì đặt cờ trong một đối tượng và sau đó áp dụng các cờ đó cho bất cứ điều gì bạn đang viết, bạn bọc lớp thực hiện cách viết trong Trình trang trí, lần lượt áp dụng các kiểu.

Mã gọi không cần biết có bao nhiêu trong số các hàm bao này bạn đặt xung quanh văn bản của bạn, bạn chỉ cần gọi một phương thức trên đối tượng bên ngoài và nó gọi xuống ngăn xếp.

Đối với một ví dụ mã giả:

class TextWriter : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // write some text somewhere somehow
        }
}

class BoldDecorator : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // bold application on
            m_textWriter.WriteString(x);
            // bold application off
        }

        ctor (TextDrawingInterface textWriter) {
            m_textWriter = textWriter;
        }

    private:
        TextWriter m_TextWriter;
}

Và như vậy, cho mỗi phong cách trang trí. Trong cách sử dụng đơn giản nhất, bạn có thể nói

TextDrawingInterface GetDecoratedTextWriter() {
    return new BoldDecorator(new ItalicDecorator(new TextWriter()));
}

Và mã gọi phương thức này không cần biết chi tiết về những gì nó nhận được. Miễn là SOMETHING có thể vẽ văn bản thông qua phương thức WriteString.


Hmmm, tôi sẽ nhìn vào ngày mai với một tâm trí tươi mới và sau đó sẽ quay lại với bạn. Cảm ơn câu trả lời.
Ếch

Tôi là một fan hâm mộ lớn của mẫu trang trí. Thực tế là mẫu này giống với HTML (trong đó hoạt động cốt lõi là kèm theo chuỗi đầu vào với các thẻ) là một bằng chứng cho thấy cách tiếp cận này có thể đáp ứng tất cả các nhu cầu của người dùng giao diện của bạn. Một điều cần lưu ý là một giao diện tốt và dễ sử dụng, có thể có một triển khai cơ bản phức tạp về mặt kỹ thuật. Ví dụ: nếu bạn thực sự tự thực hiện trình kết xuất văn bản, bạn có thể thấy TextWritercần phải nói chuyện với cả hai BoldDecoratorItalicDecorator(hai chiều) để hoàn thành công việc.
rwong

Trong khi đây là một câu trả lời hay, nó không giới thiệu lại câu hỏi của op phải không? "Ứng dụng in đậm" -> việc này sẽ được thực hiện như thế nào? sử dụng setters / getters như SetBold ()? Đó chính xác là câu hỏi của op.
stijn

1
@stijn: Không hẳn. Có một số cách để tránh tình trạng đó. Ví dụ: bạn có thể gói nó trong một trình xây dựng bằng phương thức toggleStyle (đối số enum) để thêm và xóa các trình trang trí khỏi một mảng, chỉ trang trí mục cuối cùng khi bạn gọi BuildDecoratedTextWriter. Hoặc bạn có thể làm như rwong đề xuất và làm phức tạp lớp cơ sở. Phụ thuộc vào hoàn cảnh và OP không đủ cụ thể về thông số kỹ thuật tổng thể để đoán xem cái nào là tốt nhất cho anh ta. Anh ta có vẻ đủ thông minh để có thể nhận ra điều đó một khi anh ta có được mô hình mặc dù.
pdr

1
Tôi đã luôn nghĩ rằng Mẫu trang trí ngụ ý một thứ tự cho các đồ trang trí. Xem xét mã ví dụ được hiển thị; Làm thế nào để đi về việc loại bỏ ItalicDecorator? Tuy nhiên, tôi nghĩ rằng một cái gì đó như Decorator là con đường để đi. In đậm, in nghiêng và gạch chân không phải là ba kiểu duy nhất. Có mỏng, bán nguyệt, cô đặc, đen, rộng, in nghiêng, mũ nhỏ, v.v. Bạn có thể cần một cách để áp dụng một tập hợp các kiểu lớn tùy ý cho một nhân vật.
Barry Brown

0

Tôi nghiêng về giải pháp thứ hai. Nó trông thanh lịch và linh hoạt hơn. Mặc dù thật khó để tưởng tượng các loại khác ngoài đậm, in nghiêng và gạch chân (gạch chân?), Sẽ rất khó để thêm các loại mới bằng cách sử dụng các biến thành viên.

Tôi đã sử dụng phương pháp này trong một trong những dự án mới nhất của tôi. Tôi có một lớp có thể có nhiều thuộc tính boolean. Số lượng và tên của các thuộc tính có thể thay đổi theo thời gian. Tôi lưu trữ chúng trong một từ điển. Nếu một thuộc tính không có mặt tôi giả sử giá trị của nó là "sai". Tôi cũng phải lưu trữ một danh sách các tên thuộc tính có sẵn, nhưng đó là một câu chuyện khác.


1
Ngoài các loại này, tôi sẽ thêm gạch ngang, cỡ chữ, họ phông chữ, siêu ký tự, ký tự phụ và có thể các loại khác sau này. Nhưng tại sao bạn lại chọn phương pháp đó trong dự án của mình? Bạn không nghĩ rằng đó là mã bẩn để lưu trữ một danh sách các thuộc tính được phép? Tôi thực sự tò mò câu trả lời của bạn cho những câu hỏi này là gì!
Ếch

Tôi đã phải đối phó với tình huống trong đó một số thuộc tính có thể được xác định bởi các máy khách khi ứng dụng đã được triển khai. Một số thuộc tính khác được yêu cầu cho một số khách hàng nhưng lỗi thời với những khách hàng khác. Tôi có thể tùy chỉnh các biểu mẫu, bộ dữ liệu được lưu trữ và giải quyết các vấn đề khác bằng cách sử dụng phương pháp này. Tôi không nghĩ rằng nó tạo ra mã bẩn. Đôi khi bạn không thể dự đoán loại thông tin nào sẽ cần thiết cho khách hàng của bạn.
Andrzej Bobak

Nhưng khách hàng của tôi sẽ không cần thêm thuộc tính tùy chỉnh. Và trong trường hợp đó tôi nghĩ rằng nó có vẻ hơi "hacky". Bạn không đồng ý à?
Ếch

Nếu bạn không phải đối mặt với số lượng thuộc tính động, bạn không cần không gian linh hoạt để lưu trữ chúng. Điều đó đúng :) Tôi không đồng ý với phần khác mặc dù. Theo tôi, việc sử dụng các thuộc tính trong một từ điển / hashmap / etc không phải là một cách tiếp cận tồi.
Andrzej Bobak

0

Tôi nghĩ rằng bạn đang suy nghĩ quá mức vấn đề.

styles.setBold(true)styles.setItalic(false)ổn

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.