Những thay đổi quá lớn để được thực hiện dễ dàng bằng thiết kế phù hợp?


11

Đây là một câu hỏi khá mơ hồ, nhưng đó là điều mà tôi chưa bao giờ cảm thấy đã được trả lời một cách thỏa đáng khi đọc về thiết kế phù hợp.

Nói chung, khi tìm hiểu về lập trình hướng đối tượng, trừu tượng hóa, bao thanh toán, v.v., chén thánh thiết kế - và lý do họ luôn cho rằng bạn đang sử dụng các kỹ thuật phát triển trong câu hỏi - là nó sẽ làm cho chương trình của bạn "dễ thay đổi" , "duy trì", "linh hoạt" hoặc bất kỳ từ đồng nghĩa nào được sử dụng để diễn đạt một khái niệm nghe có vẻ hiệu quả như vậy. Bằng cách đánh dấu ivars riêng tư, chia mã thành nhiều phương thức nhỏ, khép kín, giữ cho giao diện chung, bạn được cho là có khả năng sửa đổi chương trình của mình một cách dễ dàng và duyên dáng.

Đối với những thay đổi tương đối nhỏ, điều này đã làm việc tốt cho tôi. Thay đổi cấu trúc dữ liệu nội bộ được sử dụng bởi một lớp để tăng hiệu suất chưa bao giờ là một khó khăn lớn và cũng không có thay đổi nào đối với đầu giao diện người dùng, độc lập với API, như thiết kế lại hệ thống nhập văn bản hoặc đại tu đồ họa cho thành phần chơi trò chơi .

Tất cả những thay đổi này dường như khép kín. Không ai trong số họ liên quan đến bất kỳ thay đổi nào đối với hành vi hoặc thiết kế của thành phần chương trình của bạn đang được sửa đổi, theo như mã bên ngoài mà nó liên quan. Cho dù bạn có viết theo thủ tục hay theo kiểu OO, với các chức năng lớn hay nhỏ, đây là những thay đổi dễ dàng thực hiện ngay cả khi bạn chỉ có một thiết kế tốt vừa phải.

Tuy nhiên, bất cứ khi nào các thay đổi trở nên lớn và nhiều lông - nghĩa là thay đổi API - không có "mẫu" quý giá nào của tôi từng được giải cứu. Sự thay đổi lớn vẫn còn lớn, mã bị ảnh hưởng vẫn bị ảnh hưởng và nhiều giờ làm việc sinh ra lỗi nằm trước tôi.

Vì vậy, câu hỏi của tôi là này. Làm thế nào lớn thay đổi yêu cầu thiết kế phù hợp để có thể tạo điều kiện? Có một số kỹ thuật thiết kế nào khác, mà tôi không biết hoặc tôi đã không thực hiện, điều đó thực sự làm cho việc sửa đổi âm thanh trở nên đơn giản, hoặc đó là lời hứa (mà tôi đã nghe được thực hiện bởi rất nhiều mô hình khác nhau) chỉ là một ý tưởng hay, hoàn toàn mất kết nối với sự thật bất biến của phát triển phần mềm? Có "công cụ thay đổi" nào tôi có thể thêm vào toolbelt của mình không?

Cụ thể, vấn đề tôi gặp phải đã dẫn tôi đến các diễn đàn là: Tôi đã làm việc để thực hiện một ngôn ngữ lập trình được giải thích (được triển khai trong D, nhưng điều đó không liên quan), và tôi đã quyết định rằng các lập luận của tôi nên được đưa ra dựa trên từ khóa, thay vì theo vị trí như hiện tại. Điều này đòi hỏi phải sửa đổi tất cả các mã hiện có gọi các hàm ẩn danh, điều may mắn là khá nhỏ vì tôi sớm phát triển ngôn ngữ của mình (<2000 dòng), nhưng sẽ rất lớn nếu tôi đưa ra quyết định này ở giai đoạn sau. Trong tình huống như vậy, có cách nào mà bằng tầm nhìn xa phù hợp trong thiết kế tôi có thể thực hiện việc sửa đổi này dễ dàng hơn, hoặc chắc chắn (hầu hết) thay đổi về bản chất là sâu rộng? Tôi tò mò liệu đây có phải là một thất bại trong kỹ năng thiết kế của riêng tôi không - nếu có, tôi

Để rõ ràng, tôi không nghi ngờ gì về OOP hoặc bất kỳ mẫu nào khác thường được sử dụng. Đối với tôi, tuy nhiên, đức tính của họ là trong văn bản gốc, thay vì duy trì, các cơ sở mã. Kế thừa cho phép bạn mô hình hóa lặp đi lặp lại một cách rõ ràng, tính đa hình cho phép bạn tách mã theo hàm do con người hiểu (lớp nào) thay vì hiệu ứng hiểu máy (nhánh nào của switchcâu lệnh) và các hàm nhỏ, khép kín cho phép bạn viết theo kiểu "từ dưới lên" rất dễ chịu. Tuy nhiên, tôi nghi ngờ về tuyên bố linh hoạt của họ.


Tôi không thấy sự thay đổi trong ngôn ngữ của bạn chạm vào bất cứ thứ gì ngoại trừ trình phân tích cú pháp. Bạn có thể làm rõ điều này?
Scarfridge 18/03/13

Trong một ngôn ngữ giống như smalltalk, đúng là bạn chỉ cần sửa đổi trình phân tích cú pháp, bởi vì đó sẽ là một vấn đề đơn giản để xử lý foo metarg1: bar metarg2: baznhư foo.metarg1_metarg2_(bar, baz). Tuy nhiên, ngôn ngữ của tôi đang thực hiện một thay đổi lớn hơn, cụ thể là liệt kê các tham số dựa trên tham số từ điển, điều này ảnh hưởng đến thời gian chạy nhưng thực tế không phải là trình phân tích cú pháp do các thuộc tính cụ thể của ngôn ngữ của tôi mà tôi sẽ không truy cập ngay bây giờ. Vì không có ánh xạ rõ ràng giữa các đối số từ khóa không theo thứ tự và vị trí, nên thời gian chạy là vấn đề chính.
tồn tại-forall

Câu trả lời:


13

Đôi khi một thay đổi đủ lớn để bạn phải thiết kế đường dẫn di chuyển. Ngay cả khi điểm bắt đầu và điểm kết thúc được thiết kế tốt, bạn thường không thể chuyển đổi gà tây lạnh. Rất nhiều nhà thiết kế giỏi không thể thiết kế các đường di chuyển tốt.

Đây là lý do tại sao tôi nghĩ rằng mọi lập trình viên nên làm một phần mềm viết lách cho môi trường sản xuất 24/7. Có điều gì đó về những mất mát được trích dẫn theo thứ tự tiền lương hàng năm của bạn mỗi phút thúc đẩy bạn học cách viết mã di chuyển mạnh mẽ.

Những gì mọi người tự nhiên làm cho một thay đổi lớn là tách ra theo cách cũ, đưa vào cách mới, sau đó dành một vài ngày để sửa lỗi biên dịch bazillion, cho đến khi mã của bạn cuối cùng được biên dịch lại để bạn có thể bắt đầu kiểm tra. Vì mã không thể kiểm tra được trong hai ngày, bạn đột nhiên có một mớ lỗi lớn vướng víu để sắp xếp.

Đối với thay đổi thiết kế lớn như thay đổi từ đối số vị trí sang từ khóa, đường dẫn di chuyển tốt sẽ là trước tiên thêm hỗ trợ cho đối số từ khóa mà không xóa hỗ trợ vị trí . Sau đó, bạn thay đổi một cách có phương pháp mã gọi để sử dụng các đối số từ khóa. Điều này tương tự như cách bạn đã thực hiện trước đó, ngoại trừ lần này bạn có thể kiểm tra khi bạn thực hiện, bởi vì mã gọi không thay đổi vẫn hoạt động. Bởi vì các thay đổi của bạn nhỏ hơn giữa các lần kiểm tra, các lỗi dễ dàng sửa chữa sớm hơn. Sau đó, khi tất cả các mã gọi đã được thay đổi, việc loại bỏ hỗ trợ vị trí là chuyện nhỏ.

Đây là lý do tại sao các API được xuất bản công khai không dùng các phương thức cũ thay vì loại bỏ chúng gà tây lạnh. Nó cung cấp một đường dẫn di chuyển liền mạch để gọi mã. Có thể cảm thấy cần nhiều công việc hơn để hỗ trợ cả hai trong một thời gian, nhưng bạn tạo ra thời gian để thử nghiệm.

Vì vậy, để trả lời câu hỏi của bạn như cụm từ ban đầu, hầu như không có thay đổi nào quá lớn để được thực hiện dễ dàng hơn bằng thiết kế phù hợp. Nếu thay đổi của bạn có vẻ quá lớn, bạn chỉ cần thiết kế một số giai đoạn trung gian tạm thời để mã của bạn di chuyển qua.


+1 để hỗ trợ trường hợp mới và trường hợp cũ để bạn có thể loại bỏ khi bạn đi.
Erik Reppen

9

Tôi khuyên bạn nên đọc bài tiểu luận Big Ball of Mud .

Về cơ bản, điểm mà thiết kế có xu hướng xấu đi khi bạn tiến bộ với sự phát triển và bạn phải dành công việc theo hướng chứa đựng sự phức tạp. Sự phức tạp tự nó không thể được loại bỏ, chỉ có chứa, bởi vì đó là vấn đề cố hữu mà bạn đang cố gắng giải quyết.

Điều này dẫn đến các nguyên tắc chính của phát triển nhanh. Hai thứ liên quan nhất là "bạn sẽ không cần nó" bảo bạn không chuẩn bị thiết kế cho các tính năng mà bạn chưa triển khai, vì dù sao bạn cũng không thể có được thiết kế ngay và "tái cấu trúc không thương tiếc" bảo bạn làm việc về việc duy trì sự tỉnh táo của mã trong suốt dự án.

Dường như câu trả lời là không có thiết kế nào có thể tạo điều kiện cho bất kỳ thay đổi nào ngoài các ví dụ được thiết kế đặc biệt để cho thấy rằng thiết kế mạnh hơn thực tế.

Về mặt lưu ý, bạn nên nghi ngờ về lập trình hướng đối tượng. Đó là một công cụ tuyệt vời trong nhiều bối cảnh, nhưng bạn cần nhận thức được giới hạn của nó. Đặc biệt là lạm dụng quyền thừa kế có thể dẫn đến một số mã không thể tin được. Tất nhiên bạn nên hoài nghi như nhau về bất kỳ kỹ thuật thiết kế nào khác. Mỗi cái đều có công dụng và mỗi cái đều có điểm yếu. Không có viên đạn bạc.


1
+1: thiết kế trả trước mới lạ hiếm khi thành công. Các thiết kế thành công là các tóm tắt của các thiết kế đã được chứng minh hoặc được lặp đi lặp lại.
kevin cline

1
+1 bạn nên hoài nghi về tất cả các khái niệm lập trình, chỉ thông qua phê bình khách quan, chúng ta mới áp dụng các kỹ năng phân tích của mình để tìm ra giải pháp phù hợp thay vì chỉ là một giải pháp .
Jimmy Hoffa

4

Nói chung, có "các đoạn mã" (hàm, phương thức, đối tượng, bất cứ thứ gì) và "giao diện giữa các đoạn mã" (API, khai báo hàm, bất cứ điều gì, bao gồm cả hành vi).

Nếu một đoạn mã có thể được thay đổi mà không thay đổi giao diện mà các đoạn mã khác phụ thuộc vào, thì việc thay đổi sẽ dễ dàng hơn (và không quan trọng bạn có sử dụng OOP hay không).

Nếu một đoạn mã không thể thay đổi mà không thay đổi giao diện mà các đoạn mã khác phụ thuộc vào, thì thay đổi sẽ khó hơn (và không quan trọng bạn có sử dụng OOP hay không).

Lợi ích chính của OOP là "các giao diện" công cộng được đánh dấu rõ ràng (ví dụ: các phương thức công khai của một đối tượng) và các mã khác không thể sử dụng các giao diện bên trong (ví dụ: các phương thức riêng tư của đối tượng). Bởi vì các giao diện công cộng được đánh dấu rõ ràng, mọi người có xu hướng cẩn thận hơn khi thiết kế các giao diện công cộng đó (giúp tránh những thay đổi khó khăn hơn). Vì các giao diện bên trong không thể được sử dụng bởi các mã khác, bạn có thể thay đổi chúng mà không phải lo lắng về các mã khác tùy thuộc vào chúng.


Một ưu điểm khác của OOP là đóng gói dữ liệu bằng logic hoạt động trên nó dẫn đến các giao diện công cộng nhỏ hơn (nếu được thực hiện tốt).
Michael Borgwardt 18/03/13

2

Không có phương pháp thiết kế nào có thể cứu bạn khỏi những rắc rối của một yêu cầu lớn / thay đổi phạm vi. Nó chỉ là không thể tưởng tượng và tính đến tất cả các thay đổi có thể có thể được nghĩ đến vào một ngày sau đó.

Trường hợp một thiết kế tốt sẽ giúp bạn trong việc giúp bạn hiểu làm thế nào tất cả các bộ phận khớp với nhau và những gì sẽ bị ảnh hưởng bởi sự thay đổi được đề xuất.
Ngoài ra, một thiết kế tốt có thể giới hạn các phần bị ảnh hưởng bởi hầu hết các thay đổi được đề xuất.

Nói tóm lại, mọi thay đổi đều được thực hiện dễ dàng hơn bởi một thiết kế tốt, nhưng thiết kế tốt không thể biến một thay đổi lớn thành một thay đổi nhỏ. (mặt khác / không có thiết kế có thể biến bất kỳ thay đổi nhỏ nào thành lớn.)


1

Vì thiết kế dự định đưa một dự án phát triển từ không có gì thành một sản phẩm cuối cùng đáng tin cậy (ít nhất là thiết kế cho một), và đó là thay đổi lớn nhất đối với một dự án có thể có, tôi nghi ngờ có một điều như một sự thay đổi quá lớn để xử lý

Nếu bất cứ điều gì đó là đảo ngược. Một số thay đổi quá nhỏ để bận tâm với bất kỳ "thiết kế" hoặc suy nghĩ ưa thích nào. Sửa lỗi đơn giản, ví dụ.

Hãy nhớ rằng các mẫu thiết kế là để giúp suy nghĩ rõ ràng và tìm giải pháp thiết kế tốt cho các tình huống phổ biến, như tạo ra một số lượng lớn các vật thể nhỏ giống hệt nhau. Không có danh sách các mẫu được hoàn thành. Bạn và tất cả những người còn lại trong chúng ta sẽ gặp phải những vấn đề về thiết kế không phổ biến, không phù hợp với bất kỳ hố bồ câu nào. Nỗ lực thực hiện mọi động thái nhỏ trong quy trình phát triển phần mềm theo mô hình chính thức này hay mô hình chính thức khác, là một cách tôn giáo quá mức để làm mọi việc và dẫn đến những mớ hỗn độn không thể nhầm lẫn.

Tất cả các công việc trong phần mềm là sinh ra lỗi tự nhiên. Cầu thủ bóng đá bị chấn thương. Nhạc sĩ có được vết chai hoặc co thắt môi tùy thuộc vào nhạc cụ của họ. (Nếu bạn bị co thắt môi khi chơi guitar, bạn đã làm sai.) Các nhà phát triển phần mềm gặp lỗi. Chúng tôi thích tìm cách giảm tỷ lệ sinh sản của chúng, nhưng nó sẽ không bao giờ bằng không.


3
Vị trí bắt đầu cũng có thể là tiêu cực. Đừng đánh giá thấp sức mạnh của Di sản :)
Maglob 18/03/13

Thiết kế dự án từ số không là phần dễ dàng. Nhưng ngay cả khi bạn bắt đầu dự án mới từ đầu, điều này rất hiếm khi xảy ra, bạn sẽ chỉ thích xây dựng từ không có gì trong vài ngày đầu tiên. Hơn quyết định thiết kế ban đầu hóa ra là sai và mọi thứ sẽ chỉ bắt đầu xuống dốc từ đó. Thiết kế từ số 0 hoàn toàn không liên quan trong thực tế.
Jan Hudec

1

Chi phí quét thay đổi thường tỷ lệ thuận với kích thước của cơ sở mã. Do đó, việc giảm kích thước của cơ sở mã phải luôn là một trong những mục tiêu của bất kỳ thiết kế phù hợp nào.

Trong ví dụ của bạn, thiết kế phù hợp đã giúp công việc của bạn dễ dàng hơn: phải thực hiện các thay đổi trong suốt 2000 dòng cơ sở mã, thay vì cơ sở mã 20000 hoặc 200000 dòng.

Thiết kế phù hợp làm giảm các thay đổi sâu rộng, nhưng không loại bỏ chúng.

Thay đổi quét khá dễ dàng tự động nếu có sự hỗ trợ thích hợp từ các công cụ phân tích ngôn ngữ. Một thay đổi tái cấu trúc chăn có thể được áp dụng cho toàn bộ cơ sở mã theo kiểu tìm kiếm và thay thế (trong khi tôn trọng đúng các quy tắc ngôn ngữ). Lập trình viên chỉ cần đưa ra phán đoán có / không cho mỗi lần tìm kiếm.


+1 cho đề xuất tự động hóa. Tôi nghĩ rằng tôi thực sự sẽ sử dụng điều đó cho rất nhiều hàm thư viện của mình để gọi các bao đóng - đó là một vấn đề đơn giản để phát hiện các hàm nào lấy các khối và sửa đổi chúng để có một tham số ký hiệu bổ sung cho tên đối số để chuyển giá trị thành.
tồn tại-forall
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.