Lượng thời gian đáng kể, tôi không thể nghĩ ra lý do để có một đối tượng thay vì một lớp tĩnh. Các đối tượng có nhiều lợi ích hơn tôi nghĩ? [đóng cửa]


9

Tôi hiểu khái niệm về một đối tượng và là một lập trình viên Java, tôi cảm thấy mô hình OO xuất hiện khá tự nhiên đối với tôi trong thực tế.

Tuy nhiên gần đây tôi thấy mình suy nghĩ:

Đợi một chút, những lợi ích thực tế của việc sử dụng một đối tượng so với việc sử dụng một lớp tĩnh (với sự đóng gói và thực hành OO thích hợp) là gì?

Tôi có thể nghĩ về hai lợi ích của việc sử dụng một đối tượng (cả hai đều có ý nghĩa và mạnh mẽ):

  1. Đa hình: cho phép bạn trao đổi chức năng linh hoạt và linh hoạt trong thời gian chạy. Cũng cho phép dễ dàng thêm chức năng mới 'các bộ phận' và các lựa chọn thay thế cho hệ thống. Ví dụ: nếu có một Carlớp được thiết kế để làm việc với Enginecác đối tượng và bạn muốn thêm một Công cụ mới vào hệ thống mà Xe có thể sử dụng, bạn có thể tạo một Enginelớp con mới và chỉ cần truyền một đối tượng của lớp này vào Carđối tượng mà không cần phải thay đổi bất cứ điều gì về Car. Và bạn có thể quyết định làm như vậy trong thời gian chạy.

  2. Có thể 'truyền chức năng xung quanh': bạn có thể truyền một đối tượng xung quanh hệ thống một cách linh hoạt.

Nhưng có bất kỳ lợi thế hơn cho các đối tượng trên các lớp tĩnh?

Thông thường khi tôi thêm 'bộ phận' mới vào một hệ thống, tôi làm như vậy bằng cách tạo một lớp mới và khởi tạo các đối tượng từ nó.

Nhưng gần đây khi tôi dừng lại và nghĩ về nó, tôi nhận ra rằng một lớp tĩnh sẽ làm giống như một đối tượng, ở rất nhiều nơi mà tôi thường sử dụng một đối tượng.

Ví dụ: tôi đang làm việc để thêm cơ chế lưu / tải tệp vào ứng dụng của mình.

Với một đối tượng, dòng mã gọi sẽ như thế này: Thing thing = fileLoader.load(file);

Với một lớp tĩnh, nó sẽ trông như thế này: Thing thing = FileLoader.load(file);

Có gì khác biệt?

Khá thường xuyên, tôi không thể nghĩ ra lý do để khởi tạo một đối tượng khi một lớp tĩnh cũ đơn giản sẽ hoạt động giống nhau. Nhưng trong các hệ thống OO, các lớp tĩnh khá hiếm. Vì vậy, tôi phải thiếu một cái gì đó.

Có bất kỳ lợi thế hơn cho các đối tượng khác từ hai mà tôi liệt kê? Vui lòng giải thích.

EDIT: Để làm rõ. Tôi thấy các đối tượng rất hữu ích khi hoán đổi chức năng hoặc truyền dữ liệu xung quanh. Ví dụ tôi đã viết một ứng dụng tạo nên giai điệu. MelodyGeneratorcó một số lớp con tạo ra các giai điệu khác nhau và các đối tượng của các lớp này có thể hoán đổi cho nhau (mẫu Chiến lược).

Các giai điệu cũng là đối tượng, vì nó hữu ích để vượt qua chúng. Hợp âm và Cân cũng vậy.

Nhưng những gì về phần 'tĩnh' của hệ thống - điều đó sẽ không được thông qua? Ví dụ: cơ chế 'lưu tệp'. Tại sao tôi nên thực hiện nó trong một đối tượng, mà không phải là một lớp tĩnh?


Vì vậy, thay vì một đối tượng với các trường và có lẽ các phương thức, không có đối tượng, không có bản ghi, chỉ có rất nhiều giá trị vô hướng được truyền đến và trả về từ các phương thức tĩnh? Chỉnh sửa: Ví dụ mới của bạn gợi ý khác: Đối tượng, chỉ cần không có phương thức cá thể? Nếu không thì Thingsao?

Điều gì xảy ra khi bạn cần trao đổi FileLoadermột cái mà đọc từ ổ cắm? Hoặc một giả để thử nghiệm? Hoặc một trong đó mở một tập tin zip?
Benjamin Hodgson


1
@delnan Được rồi tôi đã tìm ra một câu hỏi mà câu trả lời sẽ giúp tôi hiểu: tại sao bạn lại triển khai một phần 'tĩnh' của hệ thống như một đối tượng? Giống như ví dụ trong câu hỏi: một cơ chế lưu tệp. Tôi có được gì khi thực hiện nó trong một đối tượng?
Manila Cohn

1
Mặc định nên sử dụng một đối tượng không tĩnh. Chỉ sử dụng các lớp tĩnh nếu bạn cảm thấy rằng nó thực sự thể hiện ý định của bạn tốt hơn. System.Mathtrong .NET là một ví dụ về một thứ có ý nghĩa hơn như một lớp tĩnh: bạn sẽ không bao giờ cần phải trao đổi nó hoặc chế nhạo nó và không có thao tác nào có thể trở thành một phần của một thể hiện. Tôi thực sự không nghĩ rằng ví dụ 'tiết kiệm' của bạn phù hợp với dự luật đó.
Benjamin Hodgson

Câu trả lời:


14

lol bạn nghe giống như một đội mà tôi đã từng làm việc;)

Java (và có lẽ là C #) chắc chắn hỗ trợ phong cách lập trình đó. Và tôi làm việc với những người mà bản năng đầu tiên của họ là, "Tôi có thể biến nó thành một phương thức tĩnh!" Nhưng có một số chi phí tinh tế bắt kịp với bạn theo thời gian.

1) Java là ngôn ngữ hướng đối tượng. Và nó thúc đẩy các anh chàng chức năng, nhưng thực sự nó giữ khá tốt. Ý tưởng đằng sau OO là kết hợp chức năng với trạng thái để có các đơn vị dữ liệu và chức năng nhỏ bảo tồn ngữ nghĩa của chúng bằng cách ẩn trạng thái và chỉ hiển thị các chức năng có ý nghĩa trong ngữ cảnh đó.

Bằng cách di chuyển đến một lớp chỉ có các phương thức tĩnh, bạn sẽ phá vỡ phần "trạng thái" của phương trình. Nhưng nhà nước vẫn phải sống ở đâu đó. Vì vậy, những gì tôi đã thấy theo thời gian là một lớp với tất cả các phương thức tĩnh bắt đầu có danh sách tham số ngày càng phức tạp hơn, bởi vì trạng thái đang chuyển từ lớp và vào các lệnh gọi hàm.

Sau khi bạn tạo một lớp với tất cả các phương thức tĩnh, hãy chạy qua và chỉ khảo sát xem có bao nhiêu phương thức có một tham số chung. Đó là một gợi ý rằng tham số đó phải là lớp chứa các hàm đó hoặc nếu không thì tham số đó phải là một thuộc tính của một thể hiện.

2) Các quy tắc cho OO được hiểu khá rõ. Sau một thời gian, bạn có thể nhìn vào một thiết kế lớp và xem nó có đáp ứng các tiêu chí như RẮN không. Và sau rất nhiều thử nghiệm đơn vị thực hành, bạn phát triển ý thức tốt về những gì làm cho một lớp "đúng kích cỡ" và "mạch lạc". Nhưng không có quy tắc tốt cho một lớp với tất cả các phương thức tĩnh và không có lý do thực sự tại sao bạn không nên kết hợp mọi thứ trong đó. Lớp học được mở trong trình soạn thảo của bạn, vậy cái quái gì vậy? Chỉ cần thêm phương pháp mới của bạn ở đó. Sau một thời gian, ứng dụng của bạn biến thành một số "Đối tượng Thần" cạnh tranh, mỗi đối tượng đang cố gắng thống trị thế giới. Một lần nữa, tái cấu trúc chúng thành các đơn vị nhỏ hơn rất chủ quan và khó có thể biết bạn đã làm đúng hay chưa.

3) Các giao diện là một trong những tính năng mạnh mẽ nhất của Java. Kế thừa lớp đã được chứng minh là có vấn đề, nhưng lập trình với các giao diện vẫn là một trong những thủ thuật mạnh mẽ nhất của ngôn ngữ. (ditto C #) Các lớp toàn tĩnh không thể đặt mình vào mô hình đó.

4) Nó đóng sầm cửa vào các kỹ thuật OO quan trọng mà bạn không thể tận dụng. Vì vậy, bạn có thể làm việc trong nhiều năm chỉ với một cái búa trong hộp công cụ của mình, mà không nhận ra mọi thứ sẽ dễ dàng hơn thế nào nếu bạn cũng có một tuốc nơ vít.

4.5) Nó tạo ra các phụ thuộc thời gian biên dịch khó nhất, khó phá vỡ nhất có thể. Vì vậy, ví dụ nếu bạn có FileSystem.saveFile()thì không có cách nào thay đổi điều đó, hãy rút ngắn JVM của bạn trong thời gian chạy. Điều đó có nghĩa là mọi lớp tham chiếu lớp chức năng tĩnh của bạn đều có sự phụ thuộc cứng, thời gian biên dịch vào việc triển khai cụ thể đó, điều này làm cho việc mở rộng gần như không thể và làm phức tạp việc kiểm tra. Bạn có thể kiểm tra lớp tĩnh trong sự cô lập, nhưng sẽ rất khó để kiểm tra các lớp tham chiếu đến lớp đó trong sự cô lập.

5) Bạn sẽ khiến đồng nghiệp phát điên. Hầu hết các chuyên gia mà tôi làm việc đều coi trọng mã của họ và chú ý đến ít nhất một số mức độ của các nguyên tắc thiết kế. Đặt mục đích cốt lõi của một ngôn ngữ sẽ khiến họ nhổ tóc vì họ sẽ liên tục tái cấu trúc mã.

Khi tôi ở một ngôn ngữ, tôi luôn cố gắng sử dụng một ngôn ngữ tốt. Vì vậy, ví dụ, khi tôi ở Java, tôi sử dụng thiết kế OO tốt, bởi vì sau đó tôi thực sự tận dụng ngôn ngữ cho những gì nó làm. Khi tôi ở trong Python, tôi trộn các hàm cấp mô-đun với các lớp thỉnh thoảng - tôi chỉ có thể viết các lớp bằng Python, nhưng sau đó tôi nghĩ rằng tôi sẽ không sử dụng ngôn ngữ cho những gì nó giỏi.

Một chiến thuật khác là sử dụng một ngôn ngữ tồi, và họ phàn nàn về tất cả các vấn đề mà nó gây ra. Nhưng điều đó áp dụng cho khá nhiều công nghệ.

Tính năng chính của Java là quản lý sự phức tạp trong các đơn vị nhỏ, có thể kiểm tra được kết hợp với nhau để chúng dễ hiểu. Java nhấn mạnh các định nghĩa giao diện rõ ràng độc lập với việc triển khai - đó là một lợi ích rất lớn. Đó là lý do tại sao nó (và các ngôn ngữ OO tương tự khác) vẫn được sử dụng rộng rãi như vậy. Đối với tất cả sự dài dòng và nghi thức, khi tôi thực hiện với một ứng dụng Java lớn, tôi luôn cảm thấy các ý tưởng được phân tách rõ ràng hơn trong mã so với các dự án của tôi bằng ngôn ngữ năng động hơn.

Đó là một khó khăn, mặc dù. Tôi đã thấy mọi người mắc phải lỗi "tất cả tĩnh" và thật khó để nói về vấn đề này. Nhưng tôi đã thấy họ có một cảm giác nhẹ nhõm lớn khi họ vượt qua nó.


Bạn nói chủ yếu về việc giữ mọi thứ nhỏ và mô-đun, và giữ trạng thái và chức năng cùng nhau. Không có gì ngăn cản tôi làm điều này với các lớp tĩnh. Họ có thể có nhà nước. Và bạn có thể gói gọn chúng bằng getters-setters. Và bạn có thể chia hệ thống thành các lớp nhỏ đóng gói chức năng và trạng thái. Tất cả điều này áp dụng cho cả các lớp và các đối tượng. Và đây chính xác là vấn đề nan giải của tôi: giả sử tôi đang thêm chức năng mới vào ứng dụng của mình. Rõ ràng là nó sẽ đi vào một lớp riêng để tuân theo SRP. Nhưng tại sao chính xác tôi nên khởi tạo lớp này? Đây là những gì tôi không hiểu.
Manila Cohn

Ý tôi là: đôi khi rõ ràng tại sao tôi muốn đồ vật. Tôi sẽ sử dụng ví dụ từ chỉnh sửa cho câu hỏi của mình: Tôi đã có một ứng dụng tạo nên giai điệu. Có nhiều thang điểm khác nhau mà tôi có thể cắm vào MelodyGenerators để tạo ra các giai điệu khác nhau. Và các Giai điệu là các đối tượng, vì vậy tôi có thể xếp chúng vào một ngăn xếp và quay lại chơi những cái cũ, v.v. Điều tôi không hiểu là tại sao tôi nên sử dụng các đối tượng để biểu diễn các phần 'tĩnh' của hệ thống: ví dụ: cơ chế lưu tệp. Tại sao đây không chỉ là một lớp tĩnh?
Manila Cohn

Một lớp với các phương thức tĩnh phải xuất hiện trạng thái của nó. Mà, đôi khi, là một kỹ thuật bao thanh toán rất quan trọng. Nhưng hầu hết thời gian bạn muốn đóng gói trạng thái. Một ví dụ cụ thể: nhiều năm trước, một đồng nghiệp đã viết "công cụ chọn ngày" trong Javascript. Và đó là một cơn ác mộng. Các cusomers phàn nàn về nó liên tục. Vì vậy, tôi đã tái cấu trúc nó thành các đối tượng "lịch" và đột nhiên tôi bắt đầu tạo ra nhiều đối tượng, đặt chúng cạnh nhau, nhảy giữa tháng và năm, v.v ... Thật phong phú đến nỗi chúng tôi thực sự phải tắt các tính năng. Tức thì cung cấp cho bạn quy mô đó.
Rob

3
"Ý tưởng đằng sau OO là kết hợp chức năng với trạng thái để có các đơn vị dữ liệu và chức năng nhỏ bảo tồn ngữ nghĩa của chúng bằng cách ẩn trạng thái và chỉ hiển thị các chức năng có ý nghĩa trong bối cảnh đó." Điều này về cơ bản là sai. OO không ngụ ý trạng thái có thể thay đổi và sự trừu tượng không dành riêng cho OO. Có hai cách để đạt được sự trừu tượng hóa và các đối tượng chỉ là một trong số chúng (cách còn lại là các kiểu dữ liệu trừu tượng).
Doval

1
@Doval Tôi không hiểu tại sao mọi cuộc thảo luận về OO nhất thiết phải biến thành một cuộc thảo luận về lập trình chức năng.
Rob

6

Bạn đã hỏi:

Nhưng có bất kỳ lợi thế hơn cho các đối tượng trên các lớp tĩnh?

Trước câu hỏi này, bạn đã liệt kê Đa hìnhđược thông qua là hai lợi ích của việc sử dụng Đối tượng. Tôi muốn nói rằng đó là những tính năng của mô hình OO. Đóng gói là một tính năng khác của mô hình OO.

Tuy nhiên, đó không phải là lợi ích. Những lợi ích là:

  1. Trừu tượng tốt hơn
  2. Khả năng bảo trì tốt hơn
  3. Khả năng kiểm tra tốt hơn

Bạn đã nói:

Nhưng gần đây khi tôi dừng lại và nghĩ về nó, tôi nhận ra rằng một lớp tĩnh sẽ làm giống như một đối tượng, ở rất nhiều nơi mà tôi thường sử dụng một đối tượng.

Tôi nghĩ rằng bạn có một điểm hợp lệ ở đó. Về cốt lõi, lập trình không gì khác ngoài việc chuyển đổi dữ liệu và tạo ra các tác dụng phụ dựa trên dữ liệu. Đôi khi chuyển đổi dữ liệu đòi hỏi dữ liệu phụ trợ. Những lần khác, nó không.

Khi bạn đang xử lý danh mục chuyển đổi đầu tiên, dữ liệu phụ phải được chuyển vào dưới dạng đầu vào hoặc được lưu trữ ở đâu đó. Một đối tượng là cách tiếp cận tốt hơn mà các lớp tĩnh cho các phép biến đổi đó. Một đối tượng có thể lưu trữ dữ liệu phụ trợ và sử dụng nó đúng lúc.

Đối với loại biến đổi thứ hai, một lớp tĩnh cũng tốt như đối tượng, nếu không muốn nói là tốt hơn. Các hàm toán học là ví dụ kinh điển của thể loại này. Hầu hết các chức năng thư viện C tiêu chuẩn cũng thuộc loại này.

Bạn đã hỏi:

Với một đối tượng, dòng mã gọi sẽ như thế này: Thing thing = fileLoader.load(file);

Với một lớp tĩnh, nó sẽ trông như thế này: Thing thing = FileLoader.load(file);

Có gì khác biệt?

Nếu FileLoaderkhông, bao giờ , cần lưu trữ bất kỳ dữ liệu nào, tôi sẽ đi theo cách tiếp cận thứ hai. Nếu có một cơ hội mà nó sẽ cần dữ liệu phụ trợ để thực hiện thao tác, cách tiếp cận đầu tiên là đặt cược an toàn hơn.

Bạn đã hỏi:

Có bất kỳ lợi thế hơn cho các đối tượng khác từ hai mà tôi liệt kê? Vui lòng giải thích.

Tôi đã liệt kê những lợi ích (lợi thế) của việc sử dụng mô hình OO. Tôi hy vọng họ tự giải thích. Nếu không, tôi sẽ vui mừng để giải thích.

Bạn đã hỏi:

Nhưng những gì về phần 'tĩnh' của hệ thống - điều đó sẽ không được thông qua? Ví dụ: cơ chế 'lưu tệp'. Tại sao tôi nên thực hiện nó trong một đối tượng, mà không phải là một lớp tĩnh?

Đây là một ví dụ trong đó một lớp tĩnh đơn giản sẽ không làm. Có nhiều cách để lưu dữ liệu ứng dụng của bạn vào một tệp:

  1. Lưu nó trong một tệp CSV.
  2. Lưu nó trong một tệp XML.
  3. Lưu nó trong một tập tin json.
  4. Lưu nó dưới dạng tệp nhị phân, với kết xuất trực tiếp dữ liệu.
  5. Lưu nó trực tiếp vào một số bảng trong cơ sở dữ liệu.

Cách duy nhất để cung cấp các tùy chọn như vậy là tạo Giao diện và tạo Đối tượng thực hiện giao diện.

Tóm lại là

Sử dụng phương pháp đúng cho vấn đề trong tầm tay. Tốt hơn là không nên tôn giáo về cách tiếp cận này với cách tiếp cận khác.


Ngoài ra, nếu nhiều loại đối tượng khác nhau (lớp) cần phải được lưu (ví dụ Melody, Chord, Improvisations, SoundSamplevà những người khác), sau đó nó sẽ được tiết kiệm để đơn giản hóa việc thực hiện thông qua trừu tượng.
rwong

5

Nó phụ thuộc vào ngôn ngữ và bối cảnh, đôi khi. Ví dụ: PHPcác tập lệnh được sử dụng để phục vụ các yêu cầu luôn có một yêu cầu phục vụ và một phản hồi để tạo trong toàn bộ thời gian của tập lệnh, vì vậy các phương thức tĩnh để thực hiện theo yêu cầu và tạo phản hồi có thể phù hợp. Nhưng trong một máy chủ được viết trên Node.jsđó, có thể có nhiều yêu cầu và phản hồi khác nhau cùng một lúc. Vì vậy, câu hỏi đầu tiên là - bạn có chắc chắn rằng lớp tĩnh thực sự tương ứng với một đối tượng singleton không?

Thứ hai, ngay cả khi bạn có singletons, sử dụng các đối tượng cho phép bạn tận dụng tính đa hình bằng các kỹ thuật như Dependency_injectionFactory_method_potype . Điều này thường được sử dụng trong các mẫu Inversion_of_control khác nhau và hữu ích để tạo các đối tượng giả để thử nghiệm, ghi nhật ký, v.v.

Bạn đã đề cập đến những lợi thế ở trên, vì vậy đây là một trong những điều bạn chưa làm: thừa kế. Nhiều ngôn ngữ không có khả năng ghi đè các phương thức tĩnh giống như cách các phương thức thể hiện có thể bị ghi đè. Nói chung, khó hơn nhiều để kế thừa và ghi đè các phương thức tĩnh.


Cảm ơn đã trả lời. Theo ý kiến ​​của bạn: khi tạo một phần 'tĩnh' của hệ thống, một cái gì đó sẽ không được 'truyền xung quanh' - ví dụ như phần của hệ thống phát âm thanh hoặc phần của hệ thống lưu dữ liệu vào tệp: Tôi nên thực hiện nó trong một đối tượng hoặc trong một lớp tĩnh?
Manila Cohn

3

Ngoài bài của Rob Y


Miễn là chức năng của bạn load(File file)có thể được tách biệt rõ ràng khỏi tất cả các chức năng khác mà bạn sử dụng, thì sử dụng một phương thức / lớp tĩnh là ổn. Bạn bên ngoài trạng thái (điều không phải là điều xấu) và bạn cũng không nhận được sự dư thừa vì bạn có thể áp dụng một phần hoặc áp dụng chức năng của mình để bạn không phải lặp lại. (điều đó thực sự giống hoặc tương tự với việc sử dụng một factorymẫu)

Tuy nhiên, ngay khi hai trong số các chức năng này bắt đầu có một mục đích sử dụng chung, bạn muốn có thể nhóm chúng bằng cách nào đó. Hãy tưởng tượng bạn không chỉ có một loadchức năng mà còn có một hasValidSyntaxchức năng. Bạn sẽ làm gì?

     if (FileLoader.hasValidSyntax(myfile))
          Thing thing = FileLoader.load(myfile);
     else
          println "oh noes!"

Xem hai tài liệu tham khảo myfileở đây? Bạn bắt đầu lặp lại chính mình vì trạng thái bên ngoài của bạn phải được thông qua cho mỗi cuộc gọi. Rob Y đã mô tả cách bạn tiếp thu trạng thái (ở đây file) để bạn có thể làm như thế này:

     FileLoader myfileLoader = new FileLoader(myfile)
     if (myfileLoader.hasValidSyntax())
          Thing thing = myfileLoader.load();
     else
          println "oh noes!"

Phải vượt qua trong cùng một đối tượng hai lần là một triệu chứng hời hợt; vấn đề thực tế có thể chỉ ra là một số công việc được thực hiện dự phòng - tệp có thể phải được mở hai lần, phân tích cú pháp hai lần và đóng hai lần, nếu hasValidSyntax()load()không được phép sử dụng lại trạng thái (kết quả của tệp được phân tích một phần).
rwong

2

Một vấn đề lớn khi sử dụng các lớp tĩnh là chúng buộc bạn phải ẩn các phụ thuộc của mình và buộc bạn phải phụ thuộc vào việc triển khai. Trong chữ ký của nhà xây dựng sau đây, các phụ thuộc của bạn là gì:

public Person(String name)

Chà, đánh giá từ chữ ký, một người chỉ cần một cái tên, phải không? Vâng, không phải nếu việc thực hiện là:

public Person(String name) {
    ResultSet rs = DBConnection.getPersonFilePathByName(name);
    File f = FileLoader.load(rs.getPath());
    PersonData.setDataFile(f);
    this.name = name;
    this.age = PersonData.getAge();
}

Vì vậy, một người không chỉ là ngay lập tức. Chúng tôi thực sự lấy dữ liệu từ cơ sở dữ liệu, cung cấp cho chúng tôi đường dẫn đến tệp, sau đó phải được phân tích cú pháp và sau đó dữ liệu thực mà chúng tôi muốn phải được đưa ra. Ví dụ này rõ ràng là trên đầu, nhưng nó chứng minh điểm. Nhưng chờ đợi, có thể có nhiều hơn nữa! Tôi viết bài kiểm tra sau:

public void testPersonConstructor() {
    Person p = new Person("Milhouse van Houten");
    assertEqual(10, p.age);
}

Điều này sẽ vượt qua mặc dù, phải không? Ý tôi là, chúng tôi gói gọn tất cả những thứ khác, phải không? Vâng, thực sự nó nổ tung với một ngoại lệ. Tại sao? Ồ, vâng, những phụ thuộc tiềm ẩn mà chúng ta không biết về tình trạng toàn cầu. Các DBConnectionnhu cầu khởi tạo và kết nối. Các FileLoadernhu cầu khởi tạo với một FileFormatđối tượng (như XMLFileFormathoặc CSVFileFormat). Chưa bao giờ nghe nói về những người? Vâng, đó là điểm. Mã của bạn (và trình biên dịch của bạn) không thể cho bạn biết rằng bạn cần những thứ này vì các cuộc gọi tĩnh ẩn những phụ thuộc đó. Tôi đã nói kiểm tra? Tôi có nghĩa là nhà phát triển cơ sở mới vừa xuất xưởng một cái gì đó như thế này trong bản phát hành gần đây nhất của bạn. Rốt cuộc, biên dịch = công trình, phải không?

Ngoài ra, giả sử bạn đang ở trên một hệ thống không có phiên bản MySQL đang chạy. Hoặc giả sử bạn đang sử dụng hệ thống Windows nhưng DBConnectionlớp của bạn chỉ đi ra máy chủ MySQL của bạn trên hộp Linux (có đường dẫn Linux). Hoặc giả sử bạn đang ở trên một hệ thống mà đường dẫn được trả về DBConnectionkhông được đọc / ghi cho bạn. Điều đó có nghĩa là cố gắng chạy hoặc kiểm tra hệ thống này trong bất kỳ điều kiện nào trong số những điều kiện này sẽ không thành công, không phải do lỗi mã, mà do lỗi thiết kế làm hạn chế tính linh hoạt của mã và buộc bạn phải thực hiện.

Bây giờ, giả sử, chúng tôi muốn ghi nhật ký mọi cuộc gọi vào cơ sở dữ liệu cho một Persontrường hợp cụ thể, một cuộc gọi đi qua một con đường rắc rối nhất định. Chúng tôi có thể đăng nhập DBConnection, nhưng điều này sẽ ghi lại mọi thứ , gây ra nhiều sự lộn xộn và làm cho khó phân biệt đường dẫn mã cụ thể mà chúng tôi muốn theo dõi. Tuy nhiên, nếu chúng ta đang sử dụng phép tiêm phụ thuộc với một DBConnectionthể hiện, chúng ta có thể chỉ cần thực hiện giao diện trong một trình trang trí (hoặc mở rộng lớp, vì chúng ta có cả hai tùy chọn có sẵn với một đối tượng). Với một lớp tĩnh, chúng ta không thể tạo ra sự phụ thuộc, chúng ta không thể thực hiện một giao diện, chúng ta không thể bọc nó trong một trình trang trí và chúng ta không thể mở rộng lớp. Chúng tôi chỉ có thể gọi nó trực tiếp, một nơi nào đó ẩn sâu trong mã của chúng tôi. Do đó, chúng tôi bị ép buộc để có một sự phụ thuộc ẩn vào việc thực hiện.

Điều này luôn luôn xấu? Không nhất thiết, nhưng có lẽ tốt hơn là đảo ngược quan điểm của bạn và nói "Có lý do chính đáng nào đây không phải là một ví dụ?" thay vì "Có một lý do chính đáng nào đó phải là một ví dụ?" Nếu bạn thực sự có thể nói rằng mã của bạn sẽ không thay đổi (và không trạng thái) như Math.abs(), cả trong quá trình thực hiện và theo cách nó sẽ được sử dụng, thì bạn có thể xem xét làm cho nó tĩnh. Tuy nhiên, có các trường hợp trên các lớp tĩnh, mang đến cho bạn một thế giới linh hoạt không phải lúc nào cũng dễ dàng chiếm lại sau thực tế. Nó cũng có thể cung cấp cho bạn rõ ràng hơn về bản chất thực sự của các phụ thuộc mã của bạn.


Phụ thuộc ẩn là điểm cực kỳ tốt. Tôi gần như quên mất thành ngữ của mình rằng "Chất lượng của mã nên được đánh giá từ cách sử dụng nó, chứ không phải cách nó được thực thi."
Euphoric

Nhưng phụ thuộc ẩn có thể tồn tại trong một lớp không tĩnh quá phải không? Vì vậy, tôi không thấy lý do tại sao điều này sẽ là một nhược điểm của các lớp tĩnh nhưng không phải là các lớp không tĩnh.
valenterry

@valenterry Vâng, chúng tôi cũng có thể ẩn các phụ thuộc không tĩnh, nhưng điểm quan trọng là ẩn các phụ thuộc không tĩnh là một lựa chọn . Tôi có thể newtạo ra một loạt các đối tượng trong hàm tạo (thường không được khuyến khích), nhưng tôi cũng có thể tiêm chúng. Với các lớp tĩnh, không có cách nào để tiêm chúng (dù sao không dễ dàng trong Java và đó sẽ là một cuộc thảo luận hoàn toàn khác cho các ngôn ngữ cho phép điều đó), vì vậy không có lựa chọn nào khác. Đó là lý do tại sao tôi nhấn mạnh ý tưởng bị buộc phải che giấu sự phụ thuộc quá nhiều trong câu trả lời của mình.
cbojar

Nhưng bạn có thể tiêm chúng mỗi khi bạn gọi phương thức bằng cách cung cấp chúng làm đối số. Vì vậy, bạn không bị buộc phải che giấu chúng mà buộc phải làm cho chúng rõ ràng để mọi người thấy "aha, phương pháp đó phụ thuộc vào các đối số này" chứ không phải "aha, phương pháp đó phụ thuộc vào các đối số (một phần) này và một số trạng thái xen kẽ ẩn mà tôi không biết rôi".
valenterry

Trong Java, theo hiểu biết tốt nhất của tôi, bạn không thể cung cấp một lớp tĩnh dưới dạng tham số mà không cần thông qua toàn bộ quy tắc phản xạ. (Tôi đã thử mọi cách tôi có thể nghĩ ra. Nếu bạn có cách nào đó, tôi muốn xem nó.) Và nếu bạn chọn làm điều đó, về cơ bản bạn sẽ phá hỏng hệ thống loại tích hợp (tham số là một Class, sau đó chúng tôi đảm bảo rằng đó là lớp phù hợp) hoặc bạn đang gõ vịt (chúng tôi đảm bảo rằng nó đáp ứng đúng phương thức). Nếu bạn muốn làm sau, thì bạn nên đánh giá lại lựa chọn ngôn ngữ của bạn. Nếu bạn muốn làm trước đây, các trường hợp chỉ hoạt động tốt và được tích hợp sẵn.
cbojar

1

Chỉ hai xu của tôi.

Đối với câu hỏi của bạn:

Nhưng những gì về phần 'tĩnh' của hệ thống - điều đó sẽ không được thông qua? Ví dụ: cơ chế 'lưu tệp'. Tại sao tôi nên thực hiện nó trong một đối tượng, mà không phải là một lớp tĩnh?

Bạn có thể tự hỏi mình nếu cơ chế của một phần của hệ thống phát âm thanh, hoặc một phần của hệ thống lưu dữ liệu vào một tệp SILL THAY ĐỔI . Nếu câu trả lời của bạn là có, điều đó cho thấy rằng bạn nên trừu tượng hóa chúng bằng abstract class/ interface. Bạn có thể hỏi, làm thế nào tôi có thể biết những điều trong tương lai? Chắc chắn, chúng tôi không thể. Vì vậy, nếu sự việc không trạng thái, bạn có thể sử dụng 'lớp tĩnh', chẳng hạn như java.lang.Math, sử dụng phương pháp hướng đối tượng.


Có một lời khuyên truyền miệng: Ba cuộc đình công và bạn tái cấu trúc , điều đó cho thấy rằng người ta có thể giữ cho đến khi thay đổi là thực sự cần thiết. Ý tôi là "thay đổi" là: nhiều hơn một loại đối tượng cần được lưu lại; hoặc cần lưu vào nhiều định dạng tệp (loại lưu trữ); hoặc sự gia tăng mạnh mẽ về độ phức tạp của dữ liệu được lưu. Tất nhiên, nếu một người khá chắc chắn rằng sự thay đổi sẽ sớm xảy ra, người ta có thể kết hợp một thiết kế linh hoạt hơn lên phía trước.
rwong
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.