2009-07-01 8 views
5

Giả sử tôi có các loại dữ liệu sau:Kế thừa và đóng gói các lớp học tập trong Java

class Customer { 
    String id; // unique 
    OtherCustData someOtherData; 
} 

class Service { 
    String url; // unique 
    OtherServiceData someOtherData; 
} 

class LastConnection { 
    Date date; 
    OtherConnData someOtherData; // like request or response 
} 

Bây giờ tôi cần phải nhớ khi mỗi người trong số các khách hàng kết nối với nhau của các dịch vụ.
tôi sẽ làm cho cấu trúc:

Map<Customer, Map<Service, LastConnection>> lastConnections; 

Hoặc, để có thể tìm kiếm bằng id và không phải viết tất cả các bằng() và hashCode():

Map<String, Map<String, LastConnection>> lastConnections; 

Bây giờ tôi có thể truy cập dữ liệu LastConnection của

LastConnection connection = lastConnections.get(custId).get(srvUrl); 

Tất cả điều này có vẻ xấu xí, đặc biệt là tôi phải chuyển nó thành tham số cho hàng chục phương thức mong đợi ánh xạ bản đồ của LastConnections, s o Tôi đang nghĩ đến việc tạo các lớp học của riêng tôi mà sẽ giống như thế:

class CustomerConnections extends HashMap<String, LastConnection> { 
} 

class AllConnections extends HashMap<String, CustomerConnections> { 
    public LastConnection get(String custId, String srvUrl) { 
     return get(custId).get(srvUrl); 
    } 
} 

Ok, tôi đã học đã thừa kế đó là 3v1l, vì vậy chúng ta hãy cố gắng sáng tác:

class CustomerConnections { 
    Map<String, LastConnection> customerConnections; 
    LastConnection get(String srvUrl) { 
     return customerConnections.get(srvUrl); 
    } 
    ... // all other needed operations; 
} 

class AllConnections { 
    Map<String, CustomerConnections> allConnections; 
    public LastConnection get(String custId, String srvUrl) { 
     return get(custId).get(srvUrl); 
    } 
    public CustomerConnection get(String custId) { 
     return allConnections.get(custId); 
    } 
    ... // all other needed operations; 
} 

Vấn đề là Tôi không chắc chắn những gì sẽ là cách tiếp cận tốt nhất tôn trọng các nguyên tắc SOLID và tất cả các thực hành tốt nhất. Việc tạo các lớp không làm gì ngoại trừ việc mở rộng các bộ sưu tập đã tồn tại có vẻ như nhân các thực thể ngoài sự cần thiết, nhưng sẽ làm cho mã của tôi rõ ràng hơn (Đặc biệt là khi có các cấp tiếp theo - như Bản đồ của AllConnections theo tháng và vv). Bất kỳ hướng nào?

Trả lời

5

Tạo lớp mà không làm gì cả trừ mở rộng các bộ sưu tập hiện có có vẻ như nhân các đối tượng vượt quá sự cần thiết

Tôi sẽ thay đổi để đóng gói. Bạn đang ẩn các chi tiết về cách lưu trữ thông tin này. Khách hàng của lớp học của bạn không cần biết cách bạn cung cấp cho họ lịch sử kết nối Khách hàng. Tôi nghĩ rằng đây là một ý tưởng tốt vì bạn có thể thay đổi mô hình cơ bản mà không cần khách hàng của api thay đổi mã của họ.

nhưng sẽ làm cho mã của tôi rõ ràng hơn

này là rất tốt và là một lý do chính đáng để làm điều này. YourClass.getCustomerConnection (cId) rõ ràng hơn nhiều so với yourCollection.get (id) .get (id) .getConnection().Bạn cần phải làm cho cuộc sống của những người sử dụng mã này dễ dàng hơn, ngay cả khi bạn là người đó.

(Đặc biệt là khi có những cấp độ tiếp theo - như Bản đồ của AllConnections theo tháng và vân vân)

Tốt, sau đó bạn đang có kế hoạch trước và làm mở rộng mã của bạn. Đó là thực hành tốt OO. Theo tôi, sự lúng túng mà bạn đã đạt được một mình là những gì tôi sẽ làm.

-1

Bạn có thể tạo một lớp thực hiệnMap<K,V>, và nội bộ uỷ thác cho một bản đồ container:

class CustomerConnections implements Map<String,LastConnection> { 
    private Map<String, LastConnection> customerConnections; 

    @Override 
    public LastConnection get(Object srvUrl) { 
     return customerConnections.get(srvUrl); 
    } 
    // all other needed operations; 
} 

Những điều tốt đẹp với phương pháp này là bạn có thể vượt qua xung quanh một Map trong đó có một tiêu chuẩn, cũng -hợp đồng được xác định, nhưng bạn tránh mở rộng các lớp thư viện, thường bị cau mày.

EDIT: Như đã chỉ ra dưới đây, điều này không có nhược điểm của yêu cầu bạn phải thực hiện rất nhiều phương pháp mà không phải làm gì ngắn của đại biểu để bộ sưu tập cơ bản

+2

Ngoại trừ một số ít "tất cả các hoạt động cần thiết khác" bình luận ẩn đi một số lượng cực đoan của phái đoàn lò hơi. –

+0

@Michael Điểm công bằng – butterchicken

+1

Mặc dù bạn chỉ phải thực hiện việc này một lần. Bản đồ DelegatingMap Bản đồ có thể được sử dụng làm lớp cơ sở cho tất cả các phần mở rộng như vậy. – paulcm

0

Tại sao không sử dụng custId+"#"+srvurl làm khóa trong một đơn giản Map<String, LastConnection>?

Hoặc sử dụng lớp Tuple hoặc Ghép làm khóa có chứa hai ID và triển khai hashCode()equals() - giải pháp OO "sạch" nhất.

+0

Sử dụng custId + "#" + srvUrl làm khóa sẽ không cho phép tôi nhanh chóng nhận được tất cả các kết nối của khách hàng nhất định. Nhưng nhờ ý tưởng của thư viện Tuple anyway, tôi sẽ xem xét nó. – Jakub

+0

OK, đó không phải là một phần của vấn đề ban đầu của bạn; trong trường hợp đó, Bản đồ Maps có lẽ là lựa chọn tốt nhất. –

1

Tôi sẽ tạo một đối tượng chuyên dụng để lưu trữ thông tin này. Nội dung bạn đang tạo là đối tượng , thay vì một bộ sưu tập đơn giản.

I sẽ không làm cho nó xuất phát từ Bản đồ hoặc lớp thu thập nổi tiếng khác, vì ngữ nghĩa về cách bạn lưu trữ thông tin này có thể thay đổi trong tương lai.

Thay vì thực hiện một lớp mà quan hệ cùng một khách hàng và kết nối của nó, và bên trong lớp học mà sử dụng lớp bộ sưu tập thích hợp (mà bạn đang ở tự do để thay đổi sau mà không ảnh hưởng giao diện và phần còn lại của mã của bạn)

Lớp quản lý khách hàng/kết nối của bạn không chỉ đơn giản là một vùng chứa đơn giản. Nó có thể lưu trữ siêu dữ liệu (ví dụ: khi nào mối quan hệ này được thiết lập). Nó có thể thực hiện tìm kiếm trên các kết nối được cung cấp thông tin khách hàng (nếu bạn yêu cầu). Nó có thể xử lý các bản sao theo cách bạn yêu cầu, thay vì cách lớp thu thập bên dưới xử lý chúng vv v.v. Bạn có thể dễ dàng theo dõi trong quá trình gỡ rối/ghi nhật ký/giám sát hiệu suất để dễ dàng hiểu những gì đang xảy ra.

0

Tại sao bạn duy trì mối quan hệ giữa các đối tượng bên ngoài các đối tượng đó.

tôi muốn đề nghị một cái gì đó như sau:

public class Customer 
{ 
    public List<LastConnection> getConnectionHistory() 
    { 
    ... 
    } 

    public List<LastConnection> getConnectionHistory(Service service) 
    { 
    ... 
    } 

    public List<LastConnection> getConnectionHistory(Date since) 
    { 
    ... 
    } 
} 

public class LastConnection 
{ 
    public Date getConnectionTime() 
    { 
    ... 
    } 

    public Service getService() 
    { 
    ... 
    } 
} 

Sau đó bạn vượt qua List<Customer> các phương pháp sử dụng thông tin này.

+0

Nhưng sau đó lớp khách hàng của tôi cần biết về lớp LastConnection. Tôi có thể kết thúc với tấn chu kỳ giữa các gói khác nhau. Chia tay, reusability ... Giả sử có bốn gói trong mã của tôi: Core, LastConnections, AddressBook và TransactionHistory (chỉ là các ví dụ). Nếu tôi thực hiện mypackage.core.Customer biết về (và quản lý) LastConnections, AddressBook và TransactionHistory nó sẽ tạo ra mess. Tôi thích rằng LastConnection biết về khách hàng và AddressBook biết về khách hàng và như vậy hơn so với cách khác. Bằng cách này, việc thực hiện chức năng tiếp theo sẽ không thay đổi mã hiện có. – Jakub

+0

Bởi gói tôi giả sử bạn có nghĩa là lọ? Trong trường hợp này, tôi sẽ định nghĩa các lớp miền (như mô tả ở trên) trong ứng dụng sử dụng các mối quan hệ này. Các lớp miền có thể chuyên hoặc đóng gói các lớp được đóng gói, độ nghiêng của tôi hướng tới đóng gói bởi vì bạn có thể ẩn các thuộc tính/tính năng của các lớp được đóng gói không liên quan đến ứng dụng của bạn. Tôi thường sử dụng điều này khi 'các đối tượng giá trị' có liên quan nhưng ứng dụng sẽ không nhìn thấy các bộ giải mã mà 'các đối tượng giá trị' lộ ra. Một lợi ích khác là ứng dụng của bạn bị cô lập nếu các lớp được đóng gói thay đổi. –

1

Tôi nghĩ rằng bạn cũng nên xem xét như thế nào các bộ sưu tập sẽ được sử dụng và làm thế nào các dữ liệu thu được:

  • Nếu họ là kết quả đơn giản đặt sẽ được hiển thị trên một trang (ví dụ), sau đó sử dụng các bộ sưu tập tiêu chuẩn có vẻ hợp lý - và sau đó bạn có thể sử dụng rất nhiều thư viện chuẩn để thao tác chúng.
  • Mặt khác, nếu chúng có thể thay đổi được và bất kỳ thay đổi nào cần được duy trì (ví dụ), thì việc đóng gói chúng trong lớp của bạn có thể thích hợp hơn (sau đó có thể ghi thay đổi vào cơ sở dữ liệu, v.v.).

Làm thế nào để bạn nhận và duy trì dữ liệu của mình? Nếu nó được lưu trữ trong cơ sở dữ liệu, thì có thể bạn có thể sử dụng SQL để chọn LastConnections bởi khách hàng và/hoặc dịch vụ và/hoặc tháng, thay vì phải tự bảo trì các cơ sở dữ liệu (và chỉ cần trả lại danh sách đơn giản hoặc bản đồ kết nối hoặc số lượng kết nối). Hoặc có thể bạn không muốn chạy một truy vấn cho mỗi yêu cầu, do đó, cần phải giữ toàn bộ datastructure trong bộ nhớ.

Đóng gói thường là một điều tốt, đặc biệt vì nó có thể giúp bạn tuân thủ Law of Demeter - có thể bạn có thể đẩy một số thao tác mà bạn sẽ thực hiện trên bộ sưu tập trở lại lớp AllConnections (đây là một DAO). Điều này thường có thể giúp kiểm tra đơn vị.

Ngoài ra, tại sao mở rộng HashMap được coi là ác ở đây, xem xét rằng bạn chỉ muốn thêm một phương pháp trợ giúp tầm thường? Trong mã của bạn cho AllConnections, AllConnections sẽ luôn hoạt động giống như một HashMap - nó có thể thay thế đa hình. Tất nhiên, bạn có khả năng tự khóa mình bằng cách sử dụng HashMap (thay vì TreeMap, vv), nhưng điều đó có lẽ không quan trọng vì nó có cùng phương thức công khai như Bản đồ. Tuy nhiên, cho dù bạn thực sự muốn làm điều này thực sự phụ thuộc vào cách bạn dự định sử dụng các bộ sưu tập - tôi chỉ không nghĩ rằng bạn nên tự động giảm giá thừa kế thực hiện như luôn luôn xấu (nó thường là!)

class AllConnections extends HashMap<String, CustomerConnections> { 
    public LastConnection get(String custId, String srvUrl) { 
     return get(custId).get(srvUrl); 
    } 
}