Làm thế nào để viết các hàm tạo có thể không khởi tạo đúng đối tượng


11

Đôi khi bạn cần phải viết một constructor có thể thất bại. Chẳng hạn, giả sử tôi muốn khởi tạo một đối tượng bằng đường dẫn tệp, đại loại như

obj = new Object("/home/user/foo_file")

Miễn là đường dẫn trỏ đến một tệp thích hợp thì mọi thứ đều ổn. Nhưng nếu chuỗi không phải là một đường dẫn hợp lệ, mọi thứ sẽ bị phá vỡ. Nhưng bằng cách nào?

Bạn có thể:

  1. ném một ngoại lệ
  2. trả về đối tượng null (nếu ngôn ngữ lập trình của bạn cho phép các nhà xây dựng trả về các giá trị)
  3. trả về một đối tượng hợp lệ nhưng với một cờ cho biết rằng đường dẫn của nó không được đặt đúng (ugh)
  4. khác?

Tôi giả định rằng "các thực tiễn tốt nhất" của các ngôn ngữ lập trình khác nhau sẽ thực hiện điều này khác nhau. Chẳng hạn, tôi nghĩ ObjC thích (2). Nhưng (2) sẽ không thể thực hiện trong C ++ khi các hàm tạo phải có khoảng trống làm kiểu trả về. Trong trường hợp đó tôi cho rằng (1) được sử dụng.

Trong ngôn ngữ lập trình bạn chọn, bạn có thể chỉ ra cách bạn xử lý vấn đề này và giải thích tại sao không?


1
Các ngoại lệ trong Java là một cách xử lý việc này.
Mahmoud Hossam

Các nhà xây dựng C ++ không trả về void- họ trả về một đối tượng.
gablin

6
@gablin: Thật ra, điều đó cũng không đúng. newgọi operator newđể phân bổ bộ nhớ, sau đó xây dựng để điền vào nó. Hàm tạo không trả về bất cứ thứ gì và newtrả về con trỏ mà nó nhận được operator new. Dù "không trả lại bất cứ điều gì" ngụ ý "trả lại void" là tùy thuộc vào lấy.
Jon Purdy

@Jon Purdy: Hừm, nghe có vẻ hợp lý. Cuộc gọi tốt
gablin

Câu trả lời:


8

Không bao giờ tốt khi dựa vào một nhà xây dựng để làm công việc bẩn thỉu. Bên cạnh đó, cũng không rõ ràng với một lập trình viên khác về việc công việc sẽ được thực hiện trong hàm tạo hay không trừ khi có tài liệu rõ ràng nêu rõ (và người dùng của lớp đã đọc nó hoặc được thông báo như vậy).

Ví dụ: (trong C #):

public Sprite
{
    public Sprite(string filename)
    {
    }
}

Điều gì xảy ra nếu người dùng không muốn tải tệp ngay lập tức? Điều gì nếu họ muốn làm bộ nhớ đệm theo yêu cầu của tập tin? Họ không thể. Bạn có thể nghĩ đến việc đưa một bool loadFileđối số vào hàm tạo, nhưng sau đó điều này làm cho điều này trở nên lộn xộn vì bây giờ bạn vẫn cần một Load()phương thức để tải tệp.

Với kịch bản hiện tại, nó sẽ linh hoạt và rõ ràng hơn đối với người dùng của lớp để thực hiện điều này:

Sprite sprite = new Sprite();
sprite.Load("mario.png");

Hoặc cách khác (đối với một cái gì đó như tài nguyên):

Sprite sprite = new Sprite();
sprite.Source = "mario.png";
sprite.Cache();

// On demand caching of file contents.
public void Cache()
{
     if (image == null)
     {
         try
         {
             image = new Image.FromFile(Source);
         }
         catch(...)
         {
         }
     }
}

trong trường hợp của bạn nếu tải (..) không thành công, chúng ta có đối tượng hoàn toàn vô dụng. Tôi không chắc chắn rằng tôi muốn sprite mà không có bất kỳ sprite nào ... Nhưng câu hỏi trông giống chủ đề Holywar hơn là câu hỏi thực sự :)
avtomaton

7

Trong Java, bạn có thể sử dụng các ngoại lệ hoặc sử dụng mẫu xuất xưởng, điều này sẽ cho phép bạn trả về null.

Trong Scala, bạn có thể trả về Tùy chọn [Foo] từ phương thức xuất xưởng. Điều đó cũng sẽ hoạt động trong Java, nhưng sẽ cồng kềnh hơn.


Sử dụng Ngoại lệ hoặc nhà máy trong ngôn ngữ .NET quá.
Beth Whitezel

3

Ném một ngoại lệ.

Null cần phải được kiểm tra nếu bạn có thể trả lại (và nó sẽ không được kiểm tra)

Đây là những gì ngoại lệ được kiểm tra là cho. Bạn biết nó có thể thất bại. Người gọi phải xử lý việc này.


3

Trong C ++ , các hàm tạo được sử dụng để tạo / khởi tạo các thành viên của lớp.

Không có câu trả lời đúng cho câu hỏi này. Nhưng điều mà tôi đã quan sát cho đến nay, đó là hầu hết thời gian là khách hàng (hoặc bất kỳ ai sẽ sử dụng API của bạn), người chọn cách bạn nên xử lý công cụ này.

Đôi khi, họ có thể yêu cầu bạn phân bổ tất cả các tài nguyên mà đối tượng có thể cần cho hàm tạo và ném ngoại lệ nếu có lỗi (hủy bỏ việc tạo đối tượng) hoặc không thực hiện điều đó trên hàm tạo và đảm bảo rằng việc tạo sẽ luôn thành công , để lại các nhiệm vụ này cho một số chức năng thành viên để làm.

Nếu bạn chọn hành vi, ngoại lệ là cách C ++ mặc định để xử lý lỗi và bạn nên sử dụng chúng khi bạn có thể.


1

Bạn cũng có thể khởi tạo đối tượng không có tham số hoặc chỉ có các tham số chắc chắn không bao giờ bị lỗi và sau đó bạn sử dụng hàm hoặc phương thức khởi tạo để bạn có thể ném ngoại lệ một cách an toàn hoặc làm bất cứ điều gì bạn muốn.


6
Tôi nghĩ rằng trả lại một đối tượng không sử dụng được yêu cầu khởi tạo thêm là lựa chọn tồi tệ nhất. Bây giờ bạn có một đoạn nhiều dòng phải được sao chép bất cứ khi nào lớp được sử dụng hoặc được thêm vào một phương thức mới. Bạn cũng có thể yêu cầu nhà xây dựng thực hiện tất cả các công việc và đưa ra một ngoại lệ nếu đối tượng không thể được xây dựng.
kevin cline

2
@kevin: Phụ thuộc vào đối tượng. Thất bại có thể là do tài nguyên bên ngoài nên một thực thể có thể muốn đăng ký ý định (ví dụ tên tệp) nhưng cho phép lặp lại nhiều lần để khởi tạo hoàn toàn. Đối với một đối tượng giá trị, tôi có thể nghiêng về việc báo cáo một lỗi có thể nắm bắt được lỗi cơ bản, do đó có thể đưa ra một ngoại lệ (được bao bọc?).
Dave

-4

Tôi không muốn khởi tạo bất kỳ lớp hoặc danh sách trong constructor. Khởi tạo một lớp hoặc danh sách bất cứ khi nào bạn cần. Ví dụ.

public class Test
{
    List<Employee> test = null;
}

Đừng làm điều này

public class Test
{
    List<Employee> test = null;

    public Test()
    {
        test = new List<Employee>();
    }
}

Thay vì khởi tạo khi có yêu cầu Eg.

public class Test
{
    List<Employee> emp = null;

    public Test()
    { 
    }

    public void SomeFunction()
    {
         emp = new List<Employee>();
    }
}

1
Đây là lời khuyên kém. Bạn không nên cho phép các đối tượng ở trạng thái không hợp lệ. Đó là những gì các nhà xây dựng là cho. Nếu bạn cần logic phức tạp thì hãy sử dụng mẫu nhà máy.
AlexFoxGill

1
Các nhà xây dựng ở đó để thiết lập các bất biến lớp, mã mẫu không giúp khẳng định điều đó.
Niall
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.