Đó có phải là một thực hành tốt để tránh các hằng số bằng cách sử dụng getters?


25

Đó có phải là một thực hành tốt để thay thế các hằng số được sử dụng bên ngoài các lớp bằng getters?

Ví dụ, nó là tốt hơn để sử dụng if User.getRole().getCode() == Role.CODE_ADMINhay if User.getRole().isCodeAdmin()?

Điều đó sẽ dẫn đến lớp này:

class Role {
    constant CODE_ADMIN = "admin"
    constant CODE_USER = "user"

    private code

    getRoleCode() {
       return Role.code
    }

    isCodeAdmin () {
       return Role.code == Role.CODE_ADMIN
    }

    isCodeUser () {
       return Role.code == Role.CODE_USER
    }
}

15
Tôi muốn sử dụng một cái gì đó như User.HasRole(Role.Admin).
CodeInChaos


4
Kiểm tra nguyên tắc Tell đừng hỏi .
Andy

Tôi đặt câu hỏi về tiền đề: User.getRole().getCode()đã là một bài đọc khó chịu, việc so sánh Mã với Vai trò khiến nó trở nên vô duyên hơn.
msw

Câu trả lời:


47

Trước hết, xin lưu ý rằng làm một cái gì đó giống như entity.underlyingEntity.underlyingEntity.method()được coi là mùi mã theo Luật Demeter . Bằng cách này, bạn đang tiết lộ rất nhiều chi tiết triển khai cho người tiêu dùng. Và mỗi nhu cầu mở rộng hoặc sửa đổi một hệ thống như vậy sẽ làm tổn thương rất nhiều.

Vì vậy, tôi khuyên bạn nên có một HasRolehoặc IsAdminphương pháp theo Usernhận xét của CodeInChaos. Bằng cách này, cách thức thực hiện vai trò trên người dùng vẫn là chi tiết triển khai cho người tiêu dùng. Và cũng cảm thấy tự nhiên hơn khi hỏi người dùng về vai trò của anh ta thay vì hỏi anh ta về chi tiết về vai trò của anh ta và sau đó quyết định dựa trên điều đó.


Xin vui lòng tránh sử dụng strings trừ khi cần thiết. namelà một ví dụ tốt về stringbiến vì nội dung không được biết trước. Mặt khác, một cái gì đó giống như rolenơi bạn có hai giá trị riêng biệt được biết đến tại thời điểm biên dịch, tốt hơn bạn nên sử dụng kiểu gõ mạnh. Đó là nơi loại liệt kê phát huy tác dụng ...

Đối chiếu

public bool HasRole(string role)

với

public enum Role { Admin, User }

public bool HasRole(Role role)

Trường hợp thứ hai cho tôi nhiều ý tưởng hơn về những gì tôi nên đi qua. Nó cũng ngăn tôi chuyển nhầm thành không hợp lệ stringtrong trường hợp tôi không biết gì về hằng số vai trò của bạn.


Tiếp theo là quyết định về vai trò sẽ như thế nào. Bạn có thể sử dụng enum được lưu trữ trực tiếp trên người dùng:

public enum Role
{
    Admin,
    User
}

public class User
{
    private Role _role;

    public bool HasRole(Role role)
    {
        return _role == role;
    }

    // or
    public bool IsAdmin()
    {
        return _role == Role.Admin;
    }
}

Mặt khác, nếu bạn muốn vai trò của mình có hành vi, chắc chắn nó sẽ lại ẩn các chi tiết về cách thức loại của nó được quyết định:

public enum RoleType
{
    User,
    Admin
}

public class Role
{
    private RoleType _roleType;

    public bool IsAdmin()
    {
        return _roleType == RoleType.Admin;
    }

    public bool IsUser()
    {
        return _roleType == RoleType.User;
    }

    // more role-specific logic...
}

public class User
{
    private Role _role;

    public bool IsAdmin()
    {
        return _role.IsAdmin();
    }

    public bool IsUser()
    {
        return _role.IsUser();
    }
}

Tuy nhiên, điều này khá dài dòng và sự phức tạp sẽ tăng lên với mỗi lần bổ sung vai trò - đó thường là cách mã kết thúc khi bạn cố gắng tuân thủ đầy đủ Luật của Demeter. Bạn nên cải tiến thiết kế, dựa trên các yêu cầu cụ thể của hệ thống được mô hình hóa.

Theo câu hỏi của bạn, tôi đoán bạn tốt hơn nên chọn tùy chọn đầu tiên với enum trực tiếp User. Nếu bạn cần thêm logic trên Role, tùy chọn thứ hai nên được coi là điểm bắt đầu.


10
Tôi ước rằng đây là cách mọi người sẽ làm nó ở khắp mọi nơi. Có một getter trên một thuộc tính privateatr chỉ với mục đích kiểm tra xem nó có bằng một thứ khác hay không là một thực tế khủng khiếp.
Andy

2
Re "instance.property.property.method () ..." Không phải là chất lỏng sao?
Peter Mortensen

2
@PeterMortensen Nó khác với một giao diện trôi chảy. Một giao diện lưu loát về kiểu Xchắc chắn sẽ cho phép bạn thực hiện các chuỗi lệnh gọi như thế nào X.foo().bar().fee().... Trong giao diện lưu loát này, foo, bar và phí sẽ là các hàm bên trong lớp Xtrả về một đối tượng kiểu X. Nhưng đối với 'instance.property.property.method () được đề cập trong ví dụ này, hai propertycuộc gọi thực sự sẽ nằm trong các lớp riêng biệt. Vấn đề ở đây là bạn đang trải qua một vài lớp trừu tượng để có được các chi tiết cấp thấp.
Shaz

10
Câu trả lời tốt, nhưng Luật Demeter không phải là một bài tập đếm chấm. instance.property.property.method()không nhất thiết là vi phạm hoặc thậm chí là mùi mã; nó phụ thuộc vào việc bạn đang làm việc với các đối tượng hoặc cấu trúc dữ liệu. Node.Parent.RightChild.Remove()có lẽ không vi phạm LoD (mặc dù có mùi vì những lý do khác). var role = User.Role; var roleCode = role.Code; var isAdmin = roleCode == ADMIN;gần như chắc chắn là vi phạm, mặc dù thực tế là tôi luôn "chỉ sử dụng một dấu chấm".
Carl Leth

1
Tôi hiểu rằng đó instance.property.property.method()là vi phạm LoD, nhưng OP có instance.method().method()thể ổn. Trong ví dụ cuối cùng của bạn, có rất nhiều mã soạn sẵn Userchỉ phục vụ như một mặt tiền cho Role.
Bergi

9

Dường như có một chức năng để kiểm tra xem mã được lưu trữ có phải là mã quản trị hay không. Điều bạn thực sự muốn biết là liệu người đó có phải là quản trị viên hay không. Vì vậy, nếu bạn không muốn hiển thị các hằng số, thì bạn cũng không nên tiết lộ rằng có một mã và gọi các phương thức làAdmin () và isUser ().

Điều đó nói rằng, "nếu User.getRole (). GetCode () == Role.CODE_ADMIN" thực sự là một số ít chỉ để kiểm tra xem người dùng có phải là quản trị viên hay không. Có bao nhiêu điều mà một nhà phát triển phải nhớ để viết dòng đó? Người đó phải nhớ rằng người dùng có vai trò, vai trò có mã và lớp Vai trò có hằng số cho mã. Đó là rất nhiều thông tin hoàn toàn là về việc thực hiện.


3
Điều tồi tệ hơn: Người dùng luôn có chính xác một vai trò, không hơn không kém.
Ded repeatator

5

Ngoài những gì người khác đã đăng, bạn nên nhớ rằng việc sử dụng hằng số trực tiếp có một nhược điểm khác: nếu bất cứ điều gì thay đổi cách bạn xử lý quyền người dùng, tất cả những nơi đó cũng cần phải thay đổi.

Và nó làm cho nó khủng khiếp để tăng cường. Có thể bạn muốn có một loại siêu người dùng tại một thời điểm, điều đó rõ ràng cũng có quyền quản trị. Với sự đóng gói, về cơ bản, nó là một lớp lót để thêm vào.

Nó không chỉ ngắn và sạch sẽ, nó cũng dễ sử dụng và dễ hiểu. Và - có lẽ hầu hết tất cả - thật khó để hiểu sai.


2

Mặc dù tôi phần lớn đồng ý với các đề xuất để tránh các hằng số và có một phương pháp isFoo(), v.v., một ví dụ có thể có.

Nếu có hàng trăm hằng số này và các cuộc gọi ít được sử dụng, có thể không đáng để nỗ lực viết hàng trăm phương thức isConstant1, isConstant2 ,. Trong trường hợp đặc biệt này, trường hợp bất thường, sử dụng hằng số là hợp lý.

Lưu ý rằng việc sử dụng enums hoặc hasRole()tránh sự cần thiết phải viết hàng trăm phương thức, vì vậy đó là cách tốt nhất trong tất cả các thế giới có thể.


2

Tôi không nghĩ một trong hai lựa chọn bạn trình bày là sai về cơ bản.

Tôi thấy bạn đã không đề xuất một điều mà tôi sẽ gọi thẳng là sai: mã hóa cứng các mã vai trò trong các hàm bên ngoài lớp Vai trò. Đó là:

if (user.getRole().equals("Administrator")) ...

Tôi nói chắc chắn là sai. Tôi đã thấy các chương trình làm điều này và sau đó nhận được các lỗi bí ẩn vì ai đó đã đánh vần sai chuỗi. Tôi nhớ lại một lần, một lập trình viên đã viết "stock" khi hàm đang kiểm tra "Stock".

Nếu có 100 vai trò khác nhau, tôi sẽ rất miễn cưỡng viết 100 hàm để kiểm tra mọi vai trò có thể. Có lẽ bạn sẽ tạo chúng bằng cách viết hàm đầu tiên, sau đó sao chép và dán nó 99 lần, và bạn muốn đặt cược bao nhiêu vào một trong 99 bản sao mà bạn quên cập nhật bài kiểm tra hoặc bạn sẽ thoát khỏi bài kiểm tra đó bạn đã chạy qua danh sách, vì vậy bây giờ bạn có

public bool isActuary() { return code==Role.ACTUARY; }
public bool isAccountant() { return code==Role.ACTUARY; }
... etc ...

Cá nhân, tôi cũng sẽ tránh các chuỗi cuộc gọi. Tôi muốn viết

if (user.getRole().equals(Role.FOOBATER))

sau đó

if (user.getRole().getRoleCode()==Role.FOOBATER_CODE)

và tại thời điểm đó tại sao ghi chú viết:

if (user.hasRole(Role.FOOBATER))

Sau đó cho lớp Người dùng biết cách kiểm tra vai trò.

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.