Cách để đảm bảo các trường hợp duy nhất của một lớp?


14

Tôi đang tìm kiếm các cách khác nhau để đảm bảo rằng mỗi phiên bản của một lớp nhất định là một thể hiện duy nhất có thể nhận dạng được.

Ví dụ, tôi có một Namelớp với lĩnh vực này name. Khi tôi có một Nameđối tượng được namekhởi tạo cho John Smith, tôi không muốn khởi tạo một Nameđối tượng khác với tên là John Smith, hoặc nếu việc khởi tạo xảy ra, tôi muốn tham chiếu đến đối tượng ban đầu được truyền lại hơn một đối tượng mới.

Tôi biết rằng một cách để làm điều này là có một nhà máy tĩnh chứa một Maptrong số tất cả các đối tượng Tên hiện tại và nhà máy kiểm tra xem một đối tượng có John Smith là tên không tồn tại trước khi quay lại tham chiếu đến một Namevật.

Một cách khác mà tôi có thể nghĩ ra khỏi đỉnh đầu là có một Bản đồ tĩnh trong Namelớp và khi hàm tạo được gọi là ném một ngoại lệ nếu giá trị được truyền vào nameđã được sử dụng trong một đối tượng khác, tuy nhiên tôi biết rằng sẽ ném ngoại lệ trong một nhà xây dựng nói chung là một ý tưởng tồi .

Có những cách khác để đạt được điều này?


1
Bạn muốn có một người độc thân

5
bạn nên đi với cái đầu tiên: - nhà máy tĩnh
Rohit Jain

16
@MarcB op không muốn singleton. anh ta có thể có nhiều phiên bản của cùng một lớp, nhưng những trường hợp này phải được nhận dạng.

1
@MarcB Tôi biết về mẫu đơn nhưng tôi nghĩ rằng chỉ đảm bảo một trường hợp của một lớp là có thể? Tôi muốn nhiều trường hợp, các giá trị khác nhau. Xin lỗi nếu câu hỏi không được làm rõ. chỉnh sửa: Chỉ thấy bình luận đầu tiên trước khi đăng.
Đậu phộng

2
I'm aware that one way of doing this is to have a static factory that holds a Map...Vậy tại sao bạn không muốn làm theo cách này?
Thất vọngWithFormsDesigner

Câu trả lời:


13

Thật ra bạn đã trả lời câu hỏi của bạn. Cách đầu tiên của bạn nên hiệu quả hơn ở đây. Sử dụng static factoryluôn luôn tốt hơn constructorbất cứ nơi nào bạn nghĩ rằng bạn có thể. Vì vậy, bạn có thể tránh sử dụng Constructortrong trường hợp này, nếu không bạn sẽ có throw some exceptionmột thể hiện đã tồn tại với tên đã cho.

Vì vậy, bạn có thể tạo một phương thức nhà máy tĩnh: - getInstanceWithName(name)sẽ lấy trường hợp đã có sẵn với tên đó và nếu nó không tồn tại, nó sẽ tạo một cá thể mới và đặt constructorriêng tư của bạn , vì nó chủ yếu nên được thực hiện khi xử lý static factories.

Ngoài ra, để làm điều đó, bạn cần duy trì trạng thái tĩnh Listhoặc Mapcủa tất cả các thể hiện duy nhất được tạo trong Factorylớp của bạn .

CHỈNH SỬA :

Bạn chắc chắn nên xem qua - Java hiệu quả - Mục số 1: Xem xét các nhà máy tĩnh so với các nhà xây dựng . Bạn không thể có được lời giải thích tốt hơn cuốn sách đó.


1
Đó là điều mà OP đã nghĩ đến.
BGurung

+1 cho liên kết, dường như giải quyết rõ ràng một số mối quan tâm của OP.
Robert Harvey

@RobertHarvey. Vâng, cuốn sách đó là nguồn tốt nhất cho hầu hết các chủ đề như thế này. Và đó là mục đầu tiên trong thực tế. :)
Rohit Jain

+1 cho Java hiệu quả, tôi thực sự có cuốn sách, thực sự đang ngồi trong túi của mình :) Tuy nhiên tôi chỉ tò mò về các cách khác để đạt được tính duy nhất ngoài phương thức tĩnh / phương thức tĩnh và ngoại lệ trong hàm tạo. Nếu không có thì tôi sẽ chấp nhận điều này.
Đậu phộng

@Đậu phụng. Chắc chắn rồi. Đó là tài nguyên tốt nhất để khai thác để có thêm thông tin.
Rohit Jain

5

Các đề cập về Java hiệu quả dường như thêm rất nhiều sự tin cậy vì vậy câu trả lời này dựa trên:

  • Mục 8 hiệu quả của Java: Tuân theo hợp đồng chung khi ghi đè bằng
  • Mục Java hiệu quả 9: Luôn ghi đè mã băm khi bạn ghi đè bằng
  • Mục 15 hiệu quả của Java: Giảm thiểu khả năng biến đổi

Tôi sẽ lùi lại một bước và đặt câu hỏi tại sao bạn quan tâm nếu có nhiều hơn một thể hiện của đối tượng tên này.

Tôi hiếm khi cần phải làm loại đối tượng này. Tôi đoán rằng OP đang làm điều này để họ có thể đơn giản so sánh các Nameđối tượng của họ với ==. Hoặc sử dụng các Nameđối tượng bên trong một HashMaphoặc tương tự như chìa khóa.

Nếu vậy đây là một cái gì đó có thể được giải quyết thông qua thực hiện đúngequals() .

Thích như vậy:

public final class Name {
  private final String name;

  public Name(String name) {
    if (name == null) {
      name = ""; //or exception if you like
    }
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Name)) {
      return false;
    }
    Name other = (Name) o;
    return other.name.equals(name);
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

Sau khi thực hiện như sau là đúng:

Name a = new Name("weston");
Name b = new Name("weston");
assert(a.equals(b)); //same but:
assert(a!=b); //not the same instance
//test out the Name instances in a hashmap:
HashMap<Name,Object> map = new HashMap<Name,Object>();
Object detailsIn = new Object();
map.put(a,detailsIn);
Object detailsOut = map.get(b);
assert(detailsIn==detailsOut); //the map returned the same details object
//even though we put with `a` and got with `b` thanks to our correct equals implementation

Tôi đoán mục tiêu của bạn, nhưng theo cách này bạn có thể sử dụng Namelớp trong bản đồ băm, v.v. và chúng không phải là ví dụ chính xác.


Ví dụ, xin vui lòng.
Robert Harvey

@RobertHarvey ví dụ về việc thực hiện đúng?
weston

Có vẻ như bạn đã cung cấp một. Nhưng tôi không rõ làm thế nào khác với việc chỉ kiểm tra thuộc tính Tên cho sự bình đẳng trong một phương thức nhà máy tĩnh.
Robert Harvey

1
@RobertHarvey Tôi đã cố gắng giải thích thêm, tôi đang cung cấp một giải pháp thay thế hoàn toàn không yêu cầu nhà máy tĩnh và tôi đang thách thức điểm khởi đầu của OP mà họ không muốn có nhiều hơn một lần cho mỗi tên.
weston

Một lý do hợp lệ để yêu cầu các đối tượng duy nhất là nếu bạn muốn một khối được đồng bộ hóa để sử dụng đối tượng làm màn hình. Hai đối tượng .equals () lẫn nhau sẽ không hoạt động.
Leigh Caldwell

3
  • Tạo Namegiao diện
  • Tạo giao diện NameFactoryvới phương thứcName getByName(String)
  • Tạo một triển khai NameFactoryvới Map<String,WeakReference<Name>>bên trong nó
  • synchronizetrên bản đồ theo tên bên trong getByNamephương thức trước khi tạo các thể hiện mới củaName
  • Tùy chọn, sử dụng một triển khai riêng tư tĩnh của Namegiao diện bên trong việc triển khaiNameFactory

Cách tiếp cận này sẽ cho phép bạn đảm bảo rằng:

  • Chỉ một trường hợp duy nhất Nametồn tại bất cứ lúc nào,
  • Không có rò rỉ bộ nhớ "Lingerer" trong lớp của bạn, khi Names dính xung quanh lâu hơn mức cần thiết,
  • Thiết kế vẫn có thể kiểm tra với các đối tượng bị chế giễu, bởi vì bạn sử dụng giao diện.

Bạn cũng không bị rò rỉ bộ nhớ với phương thức xuất xưởng, nếu bạn throwtồn tại tên giống nhau thay vì trả về một đối tượng hoặc nếu bạn trả lại một nullđối tượng.
Robert Harvey

@RobertHarvey Ý tôi là những rò rỉ không thực sự rò rỉ, mà là những vật thể hợp pháp (cái gọi là "kẻ nán lại"). Một bản đồ tĩnh đơn giản sẽ ngăn không cho các đối tượng giá trị của nó được phát hành, trong khi bản đồ các tham chiếu yếu sẽ chỉ giữ chúng trong bộ nhớ miễn là các tham chiếu trực tiếp khác tồn tại.
dasblinkenlight

Ah, tôi hiểu ý của bạn. Đây có thể là một trong những trường hợp hiếm hoi destructorcó ích.
Robert Harvey

1
Quá phức tạp. Nó có thể được thực hiện như câu trả lời @weston, ghi đè bằng và yêu cầu nhà máy giữ một bộ sưu tập tên.
Tulains Córdova

@ user1598390 Một người nên làm mọi thứ đơn giản nhất có thể, nhưng không đơn giản hơn. "giải pháp" của weston không giải quyết được vấn đề của OP (không có bản đồ) và một tập hợp các tên tạo ra rò rỉ bộ nhớ kéo dài (vâng, có rò rỉ bộ nhớ trong Java).
dasblinkenlight

1

bạn nên đặt các hàm tạo riêng tư và tạo các phương thức như getNameInstance(String), nếu một đối tượng có cùng tên đã tồn tại (ví dụ dựa trên một lớp tĩnh), bạn trả về tham chiếu đó, nếu không bạn tạo một đối tượng mới bằng cách sử dụng hàm tạo riêng của mình và thêm nó đến hashtable


Bạn đã lặp lại những gì tôi đã nói trong đoạn 3 của câu hỏi. Tôi đã theo những cách khác.
Đậu phộng

Tôi xin lỗi, tôi nghĩ rằng chúng tôi đã trả lời gần như cùng một lúc vì tôi đã kiểm tra câu trả lời trước khi đăng bài của tôi. Trên thực tế, tôi đã cố gắng trả lời nó trên StackOverflown và hơn là nó đã được di chuyển.
HericDenis

1

Hãy thử làm theo. Bạn phải theo dõi từng đối tượng bạn tạo ra. Đối với mục đích này, tôi đang sử dụng Danh sách. Và làm cho trình xây dựng lớp riêng tư, để kiểm tra trước có thể được áp dụng trước khi tạo một cá thể

class UniqueName
    {
        private string Name;
        public int ID;
        private static int Count=0;
        static List<UniqueName> lt=new List<UniqueName>();

        private UniqueName(string Name)
        {
            this.Name = Name;
            ID = ++Count;
        }

        public static UniqueName GetUniqueueInstance(string Name)
        {
            foreach (UniqueName un in lt)
            {
                if ( string.Compare( un.Name,Name,StringComparison.InvariantCultureIgnoreCase)==0)
                    return un;
            }

            UniqueName temp=new UniqueName(Name);
            lt.Add(temp);
            return temp;
        }
    }

Đây không phải là Java? Bạn đã viết mã giả chưa? Hoặc, trong bất kỳ ngôn ngữ khác? Vui lòng xác định rõ ràng.
Rohit Jain

Hình như C #. Akshay, bạn nên tìm hiểu các bộ sưu tập khác ngoài List. Đây sẽ là một phù hợp tốt cho Dictionary<string,UniqueName>.
weston
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.