動作設計モード(1)-責任連鎖モード、コマンドモード、割り込みモード
75584 ワード
責任連鎖モード
単一責任原則の責任と考えられる.
public static void main(String[] args) {
Request request = new Request("무궁화 꽃이 피었습니다.");
RequestHandler requestHandler = new RequestHandler();
requestHandler.handler(request);
}
public class Request {
private String body;
public Request(String body) {
this.body = body;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
public class RequestHandler {
public void handler(Request request) {
System.out.println(request.getBody());
}
}
次のコードがある場合は、検証/承認を試みます.public class RequestHandler {
public void handler(Request request) {
System.out.println("인증이 된건가");
System.out.println("이 핸들러를 사용할수있는 유저인가");
// 단일책임의 원칙을 위반한다 .
System.out.println(request.getBody());
}
}
handlerのコードを最初の方法で変更する方法があります.しかし、このやり方はSRPに違反している.
public class AuthRequestHandler extends RequestHandler {
public void handler(Request request) {
System.out.println("인증이 되었나?");
System.out.println("이 핸들러를 사용할 수 있는 유저인가?");
super.handler(request);
}
}
2つ目は新しいハンドルを作る方法です public static void main(String[] args) {
Request request = new Request("무궁화 꽃이 피었습니다.");
RequestHandler requestHandler = new AuthRequestHandler(); //변화
requestHandler.handler(request);
}
変化するクライアントコードSRPは保護できますが、まだ問題があるのは
クライアントはAutheRequestHandlerを選択する必要があります.
そして機能が追加されたら
public class LoggingRequestHandler extends RequestHandler {
@Override
public void handler(Request request) {
System.out.println("로깅");
super.handler(request);
}
}
次のレコード機能が追加された場合: public static void main(String[] args) {
Request request = new Request("무궁화 꽃이 피었습니다.");
RequestHandler requestHandler = new LoggingRequestHandler(); //변경
requestHandler.handler(request);
}
クライアントはログを再度選択する必要があります.問題は、クライアントが使用するHandlerを直接理解することです.
記録と認証をしたいならどうしますか?
責任連鎖モードを適用します.
クライアントの目標は、具体的なHandlerタイプを知らないことです.
public abstract class RequestHandler {
private RequestHandler nextHandler;
public RequestHandler(RequestHandler nextHandler) {
this.nextHandler = nextHandler;
}
public void handle(Request request) {
if (nextHandler != null) {
nextHandler.handle(request);
}
}
}
public class PrintRequestHandler extends RequestHandler{
public PrintRequestHandler(RequestHandler nextHandler) {
super(nextHandler);
}
@Override
public void handle(Request request) {
System.out.println(request.getBody());
super.handle(request);
}
}
public class AuthRequestHandler extends RequestHandler{
public AuthRequestHandler(RequestHandler nextHandler) {
super(nextHandler);
}
@Override
public void handle(Request request) {
System.out.println("인증이 되었는가?");
super.handle(request);
}
}
public class LoggingRequestHandler extends RequestHandler{
@Override
public void handle(Request request) {
System.out.println("로깅");
super.handle(request);
}
public LoggingRequestHandler(RequestHandler nextHandler) {
super(nextHandler);
}
}
public class Client {
private RequestHandler requestHandler;
public Client(RequestHandler requestHandler) {
this.requestHandler = requestHandler;
}
public void doWork() {
Request request = new Request("이번 놀이는 뽑기입니다.");
requestHandler.handle(request);
}
public static void main(String[] args) {
RequestHandler chain = new AuthRequestHandler(new LoggingRequestHandler(new PrintRequestHandler(null)));
Client client = new Client(chain);
client.doWork();
}
}
すべてのハンドルを通ることもできますし、1つのハンドルだけを起動することもできます.重要なのは、クライアントがどのハンドルを使用するかを指定する必要はありません.
これは、リクエスト/レスポンスの処理によく使用されるモードです.
長所
public static void main(String[] args) {
Filter filter = new Filter() {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 전처리
chain.doFilter(request, response);
// 후처리
}
};
}
Sebritフィルタで使用できます.要求がサーブレットに到達する前に、複数のフィルタを使用します.
最終的にはシブリットまで行きます.
@WebFilter(urlPatterns = "/hello")
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("게임에 참하신 여러분 모두 진심으로 환영합니다.");
chain.doFilter(request, response);
System.out.println("꽝!");
}
}
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
@ServletComponentScan
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
コマンドモード
リクエストをカプセル化して呼び出し者(invoker)と受信者を分離
public class Light {
private boolean isOn;
public void on() {
System.out.println("불을 켭니다.");
this.isOn = true;
}
public void off() {
System.out.println("불을 끕니다.");
this.isOn = false;
}
public boolean isOn() {
return this.isOn;
}
}
public class Button {
private Light light;
public Button(Light light) {
this.light = light;
}
public void press() {
light.off();
//버튼을 켜야한다면 이부분을 변경해야한다.
}
public static void main(String[] args) {
Button button = new Button(new Light());
button.press();
button.press();
button.press();
button.press();
}
}
public class Game {
private boolean isStarted;
public void start() {
System.out.println("게임을 시작합니다.");
this.isStarted = true;
}
public void end() {
System.out.println("게임을 종료합니다.");
this.isStarted = false;
}
public boolean isStarted() {
return isStarted;
}
}
public class MyApp {
private Game game;
public MyApp(Game game) {
this.game = game;
}
public void press() {
game.start();
}
public static void main(String[] args) {
Button button = new Button(new Light());
button.press();
button.press();
button.press();
button.press();
}
}
ライトを消したり、ゲームクラスを書いたり、ゲームコードを変更するたびにコードを変更します.ライト、ゲームはReceiver
ボタン、MyAppのコードはInvokerに相当します.
コマンドモードの適用
public interface Command {
void execute();
}
public class GameStartCommand implements Command{
private Game game;
public GameStartCommand(Game game) {
this.game = game;
}
@Override
public void execute() {
game.start();
}
}
public class GameEndCommand implements Command{
private Game game;
public GameEndCommand(Game game) {
this.game = game;
}
@Override
public void execute() {
game.end();
}
}
public class LightOnCommand implements Command{
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
public class LightOffCommand implements Command{
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
public class Button {
private Command command;
public Button(Command command) {
this.command = command;
}
public void press() {
command.execute();
}
public static void main(String[] args) {
Button button = new Button(new LightOnCommand(new Light())); //불을키고싶다.
//게임을 시작하고싶다면
//new GameStartCommand(new Game())));
button.press();
button.press();
}
}
呼び出し元のコードは変更されません.command側のコードを変更するだけでいいです.
ボタン側のコードは変更され、コマンド側のコードは
朝三暮四じゃないの?
特定の条件の下で、コマンドは変更するしかありません.
重要なのは、呼び出し元のコードが変更されないことです.
変化する部分を最小化すればいい.
長所
割り込みモード
語源は翻訳者であっても演奏者であってもよい.
public class PostfixNotation {
private final String expression;
public PostfixNotation(String expression) {
this.expression = expression;
}
public static void main(String[] args) {
PostfixNotation postfixNotation = new PostfixNotation("123+-");
postfixNotation.calculate();
}
private void calculate() {
Stack<Integer> numbers = new Stack<>();
for (char c : this.expression.toCharArray()) {
switch (c) {
case '+':
numbers.push(numbers.pop() + numbers.pop());
break;
case '-':
int right = numbers.pop();
int left = numbers.pop();
numbers.push(left - right);
break;
default:
numbers.push(Integer.parseInt(c + ""));
}
}
System.out.println(numbers.pop());
}
}
接尾辞を計算するコード接尾辞タグ式を頻繁に使用する場合は、123+-の数値だけを変更し、他の数値を追加する必要があります.
彼らに計算させる方法が必要かもしれません.
Context=公開情報が含まれている場所.グローバル価値の集積地
Expression=これらの値を参照
TerminalExpression=x,y,z値
public interface PostfixExpression {
int interpret(Map<Character, Integer> context);
}
public class PlusExpression implements PostfixExpression{
private PostfixExpression left, right;
public PlusExpression(PostfixExpression left, PostfixExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Map<Character, Integer> context) {
return left.interpret(context) + right.interpret(context);
}
}
public class MinusExpression implements PostfixExpression{
private PostfixExpression left, right;
public MinusExpression(PostfixExpression left, PostfixExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Map<Character, Integer> context) {
return left.interpret(context) - right.interpret(context);
}
}
public class PostfixParser {
public static PostfixExpression parse(String expression) {
Stack<PostfixExpression> stack = new Stack<>();
for (char c : expression.toCharArray()) {
stack.push(getExpression(c, stack));
}
return stack.pop();
}
private static PostfixExpression getExpression(char c, Stack<PostfixExpression> stack) {
switch (c) {
case '+':
return new PlusExpression(stack.pop(), stack.pop());
case '-':
PostfixExpression right = stack.pop();
PostfixExpression left = stack.pop();
return new MinusExpression(left, right);
default:
return new VariableExpression(c);
}
}
}
public class VariableExpression implements PostfixExpression{
private Character variable;
public VariableExpression(Character variable) {
this.variable = variable;
}
@Override
public int interpret(Map<Character, Integer> context) {
return context.get(variable);
}
}
public class App {
public static void main(String[] args) {
PostfixExpression expression = PostfixParser.parse("xyz+-");
int result = expression.interpret(Map.of('x', 1, 'y', 2, 'z', 3));
System.out.println(result);
}
}
エンタープライズ・モデルの利点
既存のコードを変更せずにExpressionを追加できます.短所
実施費用と頻繁に使用されるモデルかどうかを考慮すべきである.
Reference
この問題について(動作設計モード(1)-責任連鎖モード、コマンドモード、割り込みモード), 我々は、より多くの情報をここで見つけました https://velog.io/@dudwls0505/행위-디자인패턴1-책임연쇄-패턴テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol