public class Person
{
private int age;
private string name;
public Person(string name, int age)
{
this.age = age;
this.name = name;
}
}
public class Department
{
private List<Person> listofPerson;
public Department()
{
this.listofPerson = new List<Person>();
}
public void createPerson_addToList(string name, int age)
{
//person constructor được gọi ở đây
}
//một loạt hàm sẽ cần dùng Person nằm bên dưới
}
Đối với mình, mình sẽ dùng if-else vì việc kiểm tra độ dài của name rất dễ. Tại sao lại phải dùng exception trong trường hợp mình có thể xử lý được? Có giá trị trả về để người dùng biết hàm chạy thành công hay không. Nó giống kiểu mấy hàm trong SDL library, nếu chạy thất bại thì trả về -1 để người dùng biết nhập lại cái khác. Và vì lí do này nữa “Exception is very expensive”
Bạn mình lại muốn dùng exception vì 2 lí do.
Một là nếu name mà lớn hơn 32 thì cho dừng luôn, khỏi chạy nữa. Lỡ các hàm khác gọi tới object person bị null thì mệt nên cho dừng trước cho an toàn. Nói chung lại là nếu không bị exception thì khởi tạo thành công.
Hai là chỉ phải kiểm tra 1 lần trong hàm create. Sau này không phải check trong tất cả các hàm cần dùng Person xem nó có bị null hay ko (các object sẽ được kiểm tra có tồn tại hay không bằng cách kiểm tra name) => cái này mình không đồng ý vì mình sẽ có mấy hàm như getPersonAge(string name), cỡ nào cũng phải chạy hết cái list để kiểm tra xem object với tên đó có tồn tại không. Nếu không có thì làm gì tiếp? Lại phải throw exception thôi. Vậy thì dùng if cho rồi.
Câu hỏi của cậu là common design, nó không liên quan chặt chẽ tới bất cứ ngôn ngữ nào, nên tớ sẽ trả lời chung chung và không đi sâu vào việc implement bằng C# hay gì nha
Đầu tiên, tớ nghĩ bạn cậu có lý đó. Tớ sẽ giải thích thêm
Tớ sẽ hỏi ngược lại cậu điều này, tại sao phải design phức tạp các giá trị trả về của 1 hàm, khi cậu có thể dùng Exception, để chỉ ra “đây là luồng không thành công”?
Khi cậu design 1 hàm trả về các giá trị cho lỗi, ví dụ: -1 là lỗi “tên người quá dài”, -2 là lỗi “tuổi quá cao”… Cậu sẽ gặp 1 số vấn đề như sau:
Ai đó dùng method của cậu sẽ cần phải biết ý nghĩa của từng mã lỗi. Họ sẽ phải có 1 đống if tương ứng với từng mã lỗi.
Lỗi của cậu sẽ rất khó hiểu - tức là khi bất cứ ai nhìn vào lỗi của cậu, người ta không hiểu được ngay ý nghĩa của nó.
Thử tưởng tượng cậu dùng 1 method, tự dưng thấy nó trả về -1, cậu có đoán được ý nghĩa của lỗi không? Nếu cậu nhìn thấy NameIsTooLongException, cậu có đoán được ý nghĩa của lỗi không?
Cậu sẽ design thế nào nếu như method của cậu cần trả về 1 object? Return null cho việc thất bại không phải là cách hay, vì mọi người sử dụng method của cậu sẽ có tiềm năng nhận được 1 giá trị null xử lý => NullReferenceException.
Đôi khi, method gọi trực tiếp method mà cậu viết kia không cần biết lỗi gì, mà cứ foward lỗi lên tầng trên để tầng trên xử lý.
Ở các tầng trên, mỗi chỗ đôi khi chỉ xử lý 1 vài lỗi trong hệ thống trả về kia.
Giờ, nếu cậu có exception, mọi thứ sẽ ngon nghẻ, chỉ cần catch loại exception chi tiết mà cậu muốn xử lý. Nhưng nếu cậu có mã lỗi thì sao? Cậu sẽ phải document rất cẩn thận cho tất cả các method sử dụng method mà cậu viết, hoặc người khác sẽ chửi um lên khi nhìn thấy “-1” trả về từ chỗ nào đó không ai biết
Mấy cái hàm trong SDL library, thực ra lại là bad design đấy
Yep, try-catch có hơi đắt đỏ, nhưng không tới mức “rất”. Nó chỉ đắt hơn 1 chút thôi.
Cậu không định tiết kiệm vài nanosec để đổi lại, 1 đống if else bùng nhùng mà cậu và bất cứ ai sử dụng phải mất vài giờ để hiểu đấy chứ?
Cậu cũng biết truy cập mảng nhanh hơn việc dùng List object, cơ mà cậu có muốn dùng mảng để implement tất cả mọi thứ, tiết kiệm vài nanosec mỗi thao tác, hay cậu muốn tiết kiệm thời gian viết for loop, sử dụng các method tiện ích mà List object có? Engineering time costs more than computer time.
Rồi, giờ tới lượt point của bạn cậu:
Design này thực ra là đúng. Tớ nghĩ với những giải thích ở trên của tớ, cậu đã hiểu tại sao
Point này cũng đúng. Khi cậu đảm bảo dữ liệu trong list của cậu là ổn, cậu không phải lo có bất cứ giá trị null nào trong list gây ảnh hưởng tới các chức năng khác.
Nếu cậu follow điều đơn giản này, cậu sẽ hạn chế được 1 thứ, gọi là bug
Nếu không có, sao cậu không nghĩ mình trả về empty list (nếu kết quả của cậu trả về 1 list những người có tên đó), hay sử dụng Null Object pattern? Vì nó không phải là flow ngoại lệ cho chức năng chính của cậu mà
Hi vọng những điều chia sẻ trên giúp cậu hiểu được phần nào lý do sử dụng Exception.
Và tớ cũng muốn đề cập thêm, cậu nên hạn chế dùng else nhiều nhất có thể. Không tốt cho sức khỏe đâu
Lúc đang ngồi viết bài này mình cũng giác ngộ ra nhiều nhưng vẫn muốn đăng lên để xem thêm ý kiến của mọi người. @library trả lời hoàn chỉnh rồi. Cám ơn bạn.
Theo tôi vì lạm dụng exception.
Exception nó sẽ hữu ích ở nhiều chỗ nhưng không phải tất cả.
Tôi gặp nhiều đoạn code viết kiểu ẩu bằng cách bo toàn bộ code vào trong try và throw ex trong catch và không xử lý gì. Mục đích chỉ để chương trình không crash.
Hì hì, tớ nghĩ đó là vấn đề khác rồi cậu Đó là vấn đề lựa chọn giữa fail-fast (đưa lỗi ra sớm nhất nếu gặp) và fail-safe (có lỗi vẫn ỉm đi) implementation, và sự lạm dụng của try-catch.
Tớ cũng từng gặp code bo try-catch cho từng method.