ホームページ >ウェブフロントエンド >jsチュートリアル >デザインパターンの謎を解く
AI 時代が到来し、現時点では const fetch = require('node-fetch') で Node コードを吐き出すという大きな進歩です。 (今日の時点では ChatGPT と Gemini の両方に当てはまります) そして、インターネットとそのコンテンツであるサイクリック マシンのさらに別のスピンを供給します。
コンテンツの融合の中で、デザインパターンが再び現れています
Node(???) でデザイン パターンを適用する方法を説明する投稿から、Java でファクトリー パターンを適用する方法など、古いものを詳細に説明する投稿まで (2014 年 3 月にリリースされた Java 8 に Lambda が追加されました) ).
リファクタリングの第一人者に出会ったことはありますか?
これは、コンピューター サイエンス、特にプログラミングを学習する過程でおそらくアクセスした Web サイトです。このデザイン パターンのセクションは非常に詳しく説明されており、長年にわたってさまざまなフォーラムを通じて最も共有されているセクションの 1 つです。
デザイン パターンとは何かを定義すると、次のことがわかります。
デザイン パターンは、一般的な問題に対する典型的な解決策です
ソフトウェア設計で。それぞれの模様はまるで設計図のようです
特定の問題を解決するためにカスタマイズできる
コード内の設計上の問題。
では、なぜこの投稿をするのでしょうか?つまり、上にリンクした Web サイトにはたくさんの情報があります。これですべてかもしれません。
実際のところ、私はこの定義を受け入れるのにいつも苦労していました...「コード内の特定の設計上の問題を解決するため」...私のコードで?私のコードには解決する必要がある問題がありますか?
実際に何が起こるかというと、プロジェクトで使用されているプログラミング言語には抽象化が欠けている特定の「何か」をコーディングする必要があるということです。
単純明快。まだピンとこない方のために、コード付きの例をいくつか見てみましょう。これは、Java (主にオブジェクト指向プログラミング言語) でのファクトリー パターンの非常に単純な実装です。
public class ShapeFactory { public Shape createShape(String type) { if (type.equalsIgnoreCase("CIRCLE")) { return new Circle(); } else if (type.equalsIgnoreCase("SQUARE")) { return new Square(); } return null; } }その後、Java 8
(2014 年 3 月、忘れた方のために) で Lambdas (関数型プログラミングの概念) が追加されたため、代わりに次のことができるようになりました。
Map<String, Supplier<Shape>> shapeFactory = new HashMap<>(); shapeFactory.put("CIRCLE", Circle::new); shapeFactory.put("SQUARE", Square::new); Shape circle = shapeFactory.get("CIRCLE").get();ファクトリーデザインパターンは二度と必要ありません (少なくとも Java では)。
はい、ファクトリ パターンがほとんどの人が常に使用する例であることはわかっていますが、他の場合はどうなるのでしょうか?他のプログラミング言語ではどうなるでしょうか?
これは Typescript の訪問者パターンです:
interface Shape { draw(): void; accept(visitor: ShapeVisitor): void; } class Circle implements Shape { radius: number; constructor(radius: number) { this.radius = radius; } draw() { console.log("Drawing a circle"); } accept(visitor: ShapeVisitor) { visitor.visitCircle(this); } } class Square implements Shape { sideLength: number; constructor(sideLength: number) { this.sideLength = sideLength; } draw() { console.log("Drawing a square"); } accept(visitor: ShapeVisitor) { visitor.visitSquare(this); } } interface ShapeVisitor { visitCircle(circle: Circle): void; visitSquare(square: Square): void; } class AreaCalculator implements ShapeVisitor { private area = 0; visitCircle(circle: Circle) { this.area = Math.PI * circle.radius * circle.radius; console.log(`Circle area: ${this.area}`); } visitSquare(square: Square) { this.area = square.sideLength * square.sideLength; console.log(`Square area: ${this.area}`); } getArea(): number { return this.area; } } // Using the Visitor const circle = new Circle(5); const square = new Square(4); const calculator = new AreaCalculator(); circle.accept(calculator); square.accept(calculator);次のコードはまったく同じことを行っていますが、Visitor パターンの代わりに
リフレクション (実行時に独自のオブジェクトを調べて操作する言語の機能) を使用しています。
interface Shape { draw(): void; } class Circle implements Shape { // ... (same as before) radius: number; } class Square implements Shape { // ... (same as before) sideLength: number; } function calculateArea(shape: Shape) { if (shape instanceof Circle) { const circle = shape as Circle; // Type assertion const area = Math.PI * circle.radius * circle.radius; console.log(`Circle area: ${area}`); } else if (shape instanceof Square) { const square = shape as Square; // Type assertion const area = square.sideLength * square.sideLength; console.log(`Square area: ${area}`); } } const circle = new Circle(5); const square = new Square(4); calculateArea(circle); calculateArea(square);オブザーバー パターンも TypeScript にあります:
interface Observer { update(data: any): void; } class NewsPublisher { private observers: Observer[] = []; subscribe(observer: Observer) { this.observers.push(observer); } unsubscribe(observer: Observer) { this.observers = this.observers.filter(o => o !== observer); } notify(news: string) { this.observers.forEach(observer => observer.update(news)); } } class NewsletterSubscriber implements Observer { update(news: string) { console.log(`Received news: ${news}`); } } // Using the Observer const publisher = new NewsPublisher(); const subscriber1 = new NewsletterSubscriber(); const subscriber2 = new NewsletterSubscriber(); publisher.subscribe(subscriber1); publisher.subscribe(subscriber2); publisher.notify("New product launched!");同じですが、組み込み (Node API 内の) EventEmitter を使用します:
public class ShapeFactory { public Shape createShape(String type) { if (type.equalsIgnoreCase("CIRCLE")) { return new Circle(); } else if (type.equalsIgnoreCase("SQUARE")) { return new Square(); } return null; } }
その時点で、「問題」は OOP 実装にあることに気づいたかもしれません。それは非常に正しいですが、完全ではありません。
すべてのプログラミング パラダイムは、特に最も純粋な形で解釈した場合、その癖、困難、または言うなれば「直線的には達成できないもの」を持っています。
関数型プログラミングの領域に入ってみましょう。おそらくモナドについて聞いたことがあるでしょう。
あなたが数学的定義のマインドトラップに陥ったかどうかに関係なく、私たちソフトウェア開発者はモナドをデザインパターンとして理解することもできます。これは、予期せぬことが何も起こらない純粋関数の世界では副作用を考えるのが難しいためですが、ほとんどのソフトウェア製品には副作用が必要なので、どうすれば...?
これは Haskell の IO モナドの例です:
Map<String, Supplier<Shape>> shapeFactory = new HashMap<>(); shapeFactory.put("CIRCLE", Circle::new); shapeFactory.put("SQUARE", Square::new); Shape circle = shapeFactory.get("CIRCLE").get();
副作用 (ファイルの読み取り) は IO モナドに含まれています。
typescript を使用してモナドの例を追加しましょう;
interface Shape { draw(): void; accept(visitor: ShapeVisitor): void; } class Circle implements Shape { radius: number; constructor(radius: number) { this.radius = radius; } draw() { console.log("Drawing a circle"); } accept(visitor: ShapeVisitor) { visitor.visitCircle(this); } } class Square implements Shape { sideLength: number; constructor(sideLength: number) { this.sideLength = sideLength; } draw() { console.log("Drawing a square"); } accept(visitor: ShapeVisitor) { visitor.visitSquare(this); } } interface ShapeVisitor { visitCircle(circle: Circle): void; visitSquare(square: Square): void; } class AreaCalculator implements ShapeVisitor { private area = 0; visitCircle(circle: Circle) { this.area = Math.PI * circle.radius * circle.radius; console.log(`Circle area: ${this.area}`); } visitSquare(square: Square) { this.area = square.sideLength * square.sideLength; console.log(`Square area: ${this.area}`); } getArea(): number { return this.area; } } // Using the Visitor const circle = new Circle(5); const square = new Square(4); const calculator = new AreaCalculator(); circle.accept(calculator); square.accept(calculator);
古典的なものですが、おそらくモナドをインターネット上で 50 回も見たことがありますが、実際には何なのでしょうか?
解決しようとしている問題:
interface Shape { draw(): void; } class Circle implements Shape { // ... (same as before) radius: number; } class Square implements Shape { // ... (same as before) sideLength: number; } function calculateArea(shape: Shape) { if (shape instanceof Circle) { const circle = shape as Circle; // Type assertion const area = Math.PI * circle.radius * circle.radius; console.log(`Circle area: ${area}`); } else if (shape instanceof Square) { const square = shape as Square; // Type assertion const area = square.sideLength * square.sideLength; console.log(`Square area: ${area}`); } } const circle = new Circle(5); const square = new Square(4); calculateArea(circle); calculateArea(square);
オブジェクトのプロパティを定義するのを忘れていました! ?
実際のユースケースでは、これは主にデータベースやファイルからの読み取りなどの副作用からの入力になります
それでは、次のようにします:
interface Observer { update(data: any): void; } class NewsPublisher { private observers: Observer[] = []; subscribe(observer: Observer) { this.observers.push(observer); } unsubscribe(observer: Observer) { this.observers = this.observers.filter(o => o !== observer); } notify(news: string) { this.observers.forEach(observer => observer.update(news)); } } class NewsletterSubscriber implements Observer { update(news: string) { console.log(`Received news: ${news}`); } } // Using the Observer const publisher = new NewsPublisher(); const subscriber1 = new NewsletterSubscriber(); const subscriber2 = new NewsletterSubscriber(); publisher.subscribe(subscriber1); publisher.subscribe(subscriber2); publisher.notify("New product launched!");
プログラムが爆発します。
Maybe モナドを使用しない解決策:
import { EventEmitter } from 'events'; class NewsPublisher extends EventEmitter { publish(news: string) { this.emit('news', news); } } const publisher = new NewsPublisher(); publisher.on('news', (news) => { console.log(`All subscribers received the news: ${news}`); }); publisher.publish("New product launched!");
プログラムは爆発しません。
maybe モナド は、オプションの連鎖演算子があるため、JavaScript や typescript では必要ありませんが、それを実装していない言語を使用している場合は、maybe モナドまたは meybe モナドを適用できます。デザインパターンとでも言いましょうか
はい、知っています、Maybe のことを学んだばかりでそれを一度に 6 つのサイドプロジェクトに熱心に適用した人がいるのに、今では私がパーティーで「それは必要ない」と言って笑いの種になっています。ただし、まだ使用することはできます。実際、これがクールだと感じたら、ぜひ使用してください。(結局のところ、それはあなたのコードであり、そのかわいい顔は何をしても構いません! ?)
しかし、基本に立ち返ってください。他のパラダイムについてはどうですか? OOP/FP の枠にとらわれずに考えているなら、それがいいと思います。
すべてのパラダイムには、必ずしも正式に「デザイン パターン」と呼ばれるわけではないとしても、必ず独自の反復的なソリューションとテクニックがあります。
ここにいくつかの例を示します (私が考えることを避けてくれた Gemini に感謝します。きれいな書式設定と付加価値を提供してくれてありがとう?):
多くの「テクニック」と「パターン」があります。このリストは、興味がある場合に参照するスレッドを提供するためのものです。
これがお役に立てば幸いです。すぐにお読みください!
「デザイン パターン」という用語は OOP と最も密接に関連付けられていますが、他のパラダイムにも独自の反復的なソリューションとテクニックのセットがあります。これらの技術は、それらのパラダイムの特定の課題と制約に対処し、一般的な問題に対する確立されたアプローチを提供します。したがって、必ずしも正式に「デザイン パターン」と名付けられているわけではありませんが、開発者を効果的で保守可能なソリューションに導くという同様の目的を果たします。
デザイン パターンは、使用しているプログラミング言語に抽象化が欠けている機能にパッチを適用するためのよく知られた回避策として理解できます。
この投稿はほぼすべて私によって書かれており、例は Gemini 1.5 Pro によって指定されています
以上がデザインパターンの謎を解くの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。