Khi thiết kế mã, bạn có thể có hai lựa chọn.
- chỉ cần hoàn thành nó, trong trường hợp đó, mọi giải pháp sẽ giúp ích cho bạn
- mang tính mô phạm và thiết kế một giải pháp khai thác các yêu cầu của ngôn ngữ theo ý thức hệ của nó (ngôn ngữ OO trong trường hợp này - sử dụng đa hình như một phương tiện để đưa ra quyết định)
Tôi sẽ không tập trung vào phần đầu tiên của hai người, bởi vì thực sự không có gì để nói. Nếu bạn chỉ muốn làm cho nó hoạt động, bạn có thể để lại mã như vậy.
Nhưng điều gì sẽ xảy ra, nếu bạn chọn thực hiện theo cách mô phạm và thực sự giải quyết vấn đề với các mẫu thiết kế, theo cách bạn muốn nó làm?
Bạn có thể xem xét quy trình sau:
Khi thiết kế mã OO, hầu hết các if
mã trong mã không phải ở đó. Đương nhiên, nếu bạn muốn so sánh hai loại vô hướng, chẳng hạn như int
s hoặc float
s, bạn có khả năng có một if
, nhưng nếu bạn muốn thay đổi quy trình dựa trên cấu hình, bạn có thể sử dụng đa hình để đạt được những gì bạn muốn, di chuyển các quyết định ( if
s) từ logic kinh doanh của bạn đến một nơi, nơi các đối tượng được khởi tạo - đến các nhà máy .
Đến bây giờ, quy trình của bạn có thể đi qua 4 đường dẫn riêng biệt:
data
không được mã hóa hay nén (gọi không có gì, trả lại data
)
data
được nén (gọi compress(data)
và trả lại)
data
được mã hóa (gọi encrypt(data)
và trả lại)
data
được nén và mã hóa (gọi encrypt(compress(data))
và trả lại)
Chỉ cần nhìn vào 4 con đường, bạn tìm thấy một vấn đề.
Bạn có một quy trình gọi 3 (theo lý thuyết là 4, nếu bạn không gọi bất kỳ thứ gì là một) các phương thức khác nhau thao túng dữ liệu và sau đó trả về nó. Các phương thức có các tên khác nhau , khác nhau được gọi là API công khai (cách thức mà các phương thức truyền đạt hành vi của chúng).
Sử dụng mẫu bộ điều hợp , chúng tôi có thể giải quyết colision tên (chúng tôi có thể hợp nhất API công khai) đã xảy ra. Nói một cách đơn giản, bộ chuyển đổi giúp hai giao diện không tương thích làm việc cùng nhau. Ngoài ra, bộ điều hợp hoạt động bằng cách xác định giao diện bộ điều hợp mới, mà các lớp đang cố gắng hợp nhất triển khai API của chúng.
Đây không phải là một ngôn ngữ cụ thể. Đó là một cách tiếp cận chung chung, bất kỳ từ khóa nào ở đó để thể hiện nó có thể thuộc bất kỳ loại nào, trong một ngôn ngữ như C #, bạn có thể thay thế nó bằng generic ( <T>
).
Tôi sẽ giả định rằng, ngay bây giờ bạn có thể có hai lớp chịu trách nhiệm nén và mã hóa.
class Compression
{
Compress(data : any) : any { ... }
}
class Encryption
{
Encrypt(data : any) : any { ... }
}
Trong thế giới doanh nghiệp, ngay cả các lớp cụ thể này rất có thể bị thay thế bởi các giao diện, chẳng hạn như class
từ khóa sẽ được thay thế bằng interface
(bạn nên xử lý các ngôn ngữ như C #, Java và / hoặc PHP) hoặc class
từ khóa sẽ ở lại, nhưng Compress
và Encrypt
các phương thức sẽ được định nghĩa là ảo thuần , nếu bạn viết mã bằng C ++.
Để tạo một bộ chuyển đổi, chúng tôi xác định một giao diện chung.
interface DataProcessing
{
Process(data : any) : any;
}
Sau đó, chúng tôi phải cung cấp các triển khai của giao diện để làm cho nó hữu ích.
// when neither encryption nor compression is enabled
class DoNothingAdapter : DataProcessing
{
public Process(data : any) : any
{
return data;
}
}
// when only compression is enabled
class CompressionAdapter : DataProcessing
{
private compression : Compression;
public Process(data : any) : any
{
return this.compression.Compress(data);
}
}
// when only encryption is enabled
class EncryptionAdapter : DataProcessing
{
private encryption : Encryption;
public Process(data : any) : any
{
return this.encryption.Encrypt(data);
}
}
// when both, compression and encryption are enabled
class CompressionEncryptionAdapter : DataProcessing
{
private compression : Compression;
private encryption : Encryption;
public Process(data : any) : any
{
return this.encryption.Encrypt(
this.compression.Compress(data)
);
}
}
Bằng cách này, bạn kết thúc với 4 lớp, mỗi lớp làm một thứ hoàn toàn khác nhau, nhưng mỗi lớp cung cấp cùng một API công khai. Các Process
phương pháp.
Trong logic kinh doanh của bạn, nơi bạn xử lý quyết định không / mã hóa / nén / cả hai, bạn sẽ thiết kế đối tượng của mình để làm cho nó phụ thuộc vào DataProcessing
giao diện mà chúng ta đã thiết kế trước đó.
class DataService
{
private dataProcessing : DataProcessing;
public DataService(dataProcessing : DataProcessing)
{
this.dataProcessing = dataProcessing;
}
}
Quá trình tự nó có thể đơn giản như thế này:
public ComplicatedProcess(data : any) : any
{
data = this.dataProcessing.Process(data);
// ... perhaps work with the data
return data;
}
Không còn điều kiện nữa. Lớp DataService
không biết điều gì sẽ thực sự được thực hiện với dữ liệu khi nó được chuyển cho dataProcessing
thành viên và nó không thực sự quan tâm đến nó, đó không phải là trách nhiệm của nó.
Lý tưởng nhất là bạn sẽ có các bài kiểm tra đơn vị kiểm tra 4 lớp bộ điều hợp bạn đã tạo để đảm bảo chúng hoạt động, bạn thực hiện bài kiểm tra của mình. Và nếu họ vượt qua, bạn có thể khá chắc chắn rằng họ sẽ làm việc bất kể bạn gọi họ ở đâu trong mã của bạn.
Vì vậy, làm theo cách này tôi sẽ không bao giờ có if
s trong mã của mình nữa?
Không. Bạn ít có điều kiện trong logic kinh doanh của mình, nhưng họ vẫn phải ở đâu đó. Nơi là nhà máy của bạn.
Và điều này là tốt. Bạn tách các mối quan tâm của việc tạo và thực sự sử dụng mã. Nếu bạn làm cho các nhà máy của mình trở nên đáng tin cậy (bằng Java, bạn thậm chí có thể sử dụng một cái gì đó giống như khung Guice của Google), theo logic kinh doanh của bạn, bạn không lo lắng về việc chọn đúng loại được tiêm. Bởi vì bạn biết các nhà máy của bạn làm việc và sẽ cung cấp những gì được yêu cầu.
Có cần thiết phải có tất cả các lớp, giao diện, vv?
Điều này đưa chúng ta trở lại sự khởi đầu.
Trong OOP, nếu bạn chọn đường dẫn sử dụng đa hình, thực sự muốn sử dụng các mẫu thiết kế, muốn khai thác các tính năng của ngôn ngữ và / hoặc muốn theo mọi thứ là một ý thức hệ đối tượng, thì đó là. Và thậm chí sau đó, ví dụ này thậm chí không hiển thị tất cả các nhà máy bạn sẽ cần và nếu bạn đã cấu trúc lại các Compression
và Encryption
các lớp học và làm cho họ giao diện thay vào đó, bạn phải bao gồm việc triển khai của họ là tốt.
Cuối cùng, bạn kết thúc với hàng trăm lớp học và giao diện nhỏ, tập trung vào những thứ rất cụ thể. Điều này không hẳn là xấu, nhưng có thể không phải là giải pháp tốt nhất cho bạn nếu tất cả những gì bạn muốn là làm một việc đơn giản như thêm hai số.
Nếu bạn muốn làm cho nó thực hiện và nhanh chóng, bạn có thể lấy giải pháp Ixrec của , người ít nhất được quản lý để loại bỏ else if
và else
ngăn chặn, trong đó, theo ý kiến của tôi, thậm chí còn tồi tệ hơn một chút so với một đồng bằng if
.
Hãy xem xét đây là cách của tôi để làm cho thiết kế OO tốt. Viết mã cho các giao diện thay vì triển khai, đây là cách tôi đã thực hiện trong vài năm qua và đó là cách tiếp cận tôi cảm thấy thoải mái nhất.
Cá nhân tôi thích lập trình if-less hơn và sẽ đánh giá cao giải pháp dài hơn trong 5 dòng mã. Đó là cách tôi quen với việc thiết kế mã và rất thoải mái khi đọc nó.
Cập nhật 2: Đã có một cuộc thảo luận sôi nổi về phiên bản đầu tiên của giải pháp của tôi. Thảo luận chủ yếu là do tôi, mà tôi xin lỗi.
Tôi quyết định chỉnh sửa câu trả lời theo cách đó là một trong những cách để xem xét giải pháp nhưng không phải là cách duy nhất. Tôi cũng loại bỏ phần trang trí, thay vào đó tôi có nghĩa là mặt tiền, mà cuối cùng tôi quyết định bỏ đi hoàn toàn, bởi vì một bộ chuyển đổi là một biến thể mặt tiền.
if
tuyên bố mới ?