これは、具体的な例がないと説明するのが難しいものです。Robert Martin の著書「Agile Software Development, Principles, Patterns, and Practices」を読むことをお勧めします。この本は、Open-Close 原則の由来でもあります。
豊富な動作を持つドメイン オブジェクトは、Open-Close 原則と競合しません。それらに動作がない場合、合理的な拡張機能を作成することはできません。オープンクローズの原則を適用する鍵は、将来の変化を予測し、役割を果たし、単一の責任を維持するための新しいインターフェイスを作成することです。
開閉原理を実際のコードに適用する話をします。うまくいけば、それは役に立ちます。
最初にメッセージを送信する Sender クラスがありました。
package com.thinkinginobjects;
public class MessageSender {
private Transport transport;
public void send(Message message) {
byte[] bytes = message.toBytes();
transport.sendBytes(bytes);
}
}
ある日、10 件のメッセージをまとめて送信するように依頼されました。簡単な解決策は次のとおりです。
パッケージcom.thinkinginobjects;
public class MessageSenderWithBatch {
private static final int BATCH_SIZE = 10;
private Transport transport;
private List<Message> buffer = new ArrayList<Message>();
public void send(Message message) {
buffer.add(message);
if (buffer.size() == BATCH_SIZE) {
for (Message each : buffer) {
byte[] bytes = each.toBytes();
transport.sendBytes(bytes);
}
buffer.clear();
}
}
}
しかし、私の経験では、これで終わりではないかもしれないと教えてくれました。メッセージをバッチ処理する別の方法が必要になると予想しました。したがって、バッチ戦略を作成し、送信者にそれを使用させました。ここで開閉原理を適用していたことに注意してください。将来、新しい種類のバッチ戦略があった場合、私のコードは (新しい BatchStrategy を追加することによって) 拡張可能ですが、(既存のコードを変更しないことによって) 変更に近いものになります。ただし、Robert Martin が彼の著書で述べているように、コードがいくつかの種類の変更に対して開かれている場合、それは他の種類の変更にも近いものです。誰かが将来送信された後にコンポーネントに通知したい場合、私のコードはこの種の変更に対して開かれていません。
package com.thinkinginobjects;
public class MessageSenderWithStrategy {
private Transport transport;
private BatchStrategy strategy;
public void send(Message message) {
strategy.newMessage(message);
List<Message> messages = strategy.getMessagesToSend();
for (Message each : messages) {
byte[] bytes = each.toBytes();
transport.sendBytes(bytes);
}
strategy.sent();
}
}
package com.thinkinginobjects;
public class FixSizeBatchStrategy implements BatchStrategy {
private static final int BATCH_SIZE = 0;
private List<Message> buffer = new ArrayList<Message>();
@Override
public void newMessage(Message message) {
buffer.add(message);
}
@Override
public List<Message> getMessagesToSend() {
if (buffer.size() == BATCH_SIZE) {
return buffer;
} else {
return Collections.emptyList();
}
}
@Override
public void sent() {
buffer.clear();
}
}
ストーリーを完成させるために、数日後、バッチメッセージを 5 秒ごとに送信するよう要求を受けました。私の推測は正しかったので、コードを変更する代わりに拡張機能を追加することで要件に対応できます。
package com.thinkinginobjects;
public class FixIntervalBatchStrategy implements BatchStrategy {
private static final long INTERVAL = 5000;
private List<Message> buffer = new ArrayList<Message>();
private volatile boolean readyToSend;
public FixIntervalBatchStrategy() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
readyToSend = true;
}
}, 0, INTERVAL, TimeUnit.MILLISECONDS);
}
@Override
public void newMessage(Message message) {
buffer.add(message);
}
@Override
public List<Message> getMessagesToSend() {
if (readyToSend) {
return buffer;
} else {
return Collections.emptyList();
}
}
@Override
public void sent() {
readyToSend = false;
buffer.clear();
}
}
- 免責事項: コード例は www.thinkingInObjects.com に属します