Heim >Web-Frontend >js-Tutorial >Beherrschen des Builder-Musters: Erstellen Sie eine dynamische AI-Prompt-Generator-CLI
Ist Ihnen auf Ihrer Entwicklungsreise schon einmal ein Fall begegnet, in dem Sie mit komplexen Objekten zu tun hatten? Vielleicht, weil sie entweder zu viele Parameter haben, die sogar verschachtelt werden können, oder weil sie viele Bauschritte und eine komplexe Logik zum Aufbau erfordern.
Vielleicht möchten Sie ein Modul mit einer übersichtlichen und einfachen Benutzeroberfläche entwerfen, ohne jedes Mal den Erstellungscode Ihrer komplexen Objekte zerstreuen oder darüber nachdenken zu müssen!
Hier kommt das Builder-Designmuster ins Spiel!
In diesem Tutorial erklären wir alles über das Builder-Entwurfsmuster. Anschließend erstellen wir eine CLI-Node.js-Anwendung zum Generieren einer DALL-E 3-optimierten Bildgenerierungsaufforderung unter Verwendung des Builder-Entwurfsmusters .
Der endgültige Code ist in diesem Github-Repository verfügbar.
Builder ist ein kreatives Designmuster, eine Kategorie von Designmustern, die sich mit den verschiedenen Problemen befasst, die mit der nativen Art der Objekterstellung mit dem Neuen einhergehen Schlüsselwort oder Operator.
Das Builder Design Pattern konzentriert sich auf die Lösung der folgenden Probleme:
Bereitstellung einer einfachen Schnittstelle zum Erstellen komplexer Objekte: Stellen Sie sich ein tief verschachteltes Objekt mit vielen erforderlichen Initialisierungsschritten vor.
Trennung des Konstruktionscodes vom Objekt selbst, was die Erstellung mehrerer Darstellungen oder Konfigurationen aus demselben Objekt ermöglicht.
Das Builder Design Pattern löst diese beiden Probleme, indem es die Verantwortung für die Objekterstellung an spezielle Objekte namens Builder delegiert.
Das Builder-Objekt setzt das Originalobjekt zusammen und unterteilt den Erstellungsprozess in mehrere Phasen oder Schritte.
Jeder Schritt wird durch eine Methode im Builder-Objekt definiert, die eine Teilmenge der Objektattribute basierend auf einer Geschäftslogik initialisiert.
class PromptBuilder { private prompt: Prompt constructor() { this.reset() } reset() { this.prompt = new Prompt() } buildStep1() { this.prompt.subject = "A cheese eating a burger" //initialization code... return this } buildStep2() { //initialization code... return this } buildStep3() { //initialization code... return this } build() { const result = structuredClone(this.prompt) // deep clone this.reset() return result } }
Client-Code: Wir müssen nur den Builder verwenden und die einzelnen Schritte aufrufen
const promptBuilder = new PromptBuilder() const prompt1 = promptBuilder .buildStep1() // optional .buildStep2() // optional .buildStep3() // optional .build() // we've got a prompt const prompt2 = promptBuilder .buildStep1() // optional .buildStep3() // optional .build() // we've got a prompt
Das typische Builder-Entwurfsmuster besteht aus 4 Hauptklassen:
Builder: Die Builder-Schnittstelle sollte nur die Konstruktionsmethoden definieren, ohne die Methode build(), die für die Rückgabe der erstellten Entität verantwortlich ist.
Concrete Builder-Klassen: Jeder konkrete Builder stellt seine eigene Implementierung der Builder Interface-Methoden bereit, sodass er seine eigene Variante des Objekts (Instanz von Produkt1 oder Produkt2 ).
Client: Sie können sich den Client als den Top-Level-Konsumenten unserer Objekte, den Benutzer, der Bibliotheksmodule importiert, oder den Einstiegspunkt unserer Anwendung vorstellen.
Direktor: Sogar das gleiche Builder-Objekt kann viele Varianten des Objekts erzeugen.
class PromptBuilder { private prompt: Prompt constructor() { this.reset() } reset() { this.prompt = new Prompt() } buildStep1() { this.prompt.subject = "A cheese eating a burger" //initialization code... return this } buildStep2() { //initialization code... return this } buildStep3() { //initialization code... return this } build() { const result = structuredClone(this.prompt) // deep clone this.reset() return result } }Wie Sie dem obigen Code entnehmen können, besteht ein großer Bedarf an einer Entität, die die Verantwortung für die Steuerung oder Orchestrierung der verschiedenen möglichen Kombinationssequenzen von Aufrufen der Builder-Methoden übernimmt, da jede Sequenz ein anderes resultierendes Objekt erzeugen kann.
Können wir den Prozess also weiter abstrahieren und eine noch einfachere Schnittstelle für den Client-Code bereitstellen?
Hier kommt die
Director-Klasse ins Spiel. Der Director übernimmt mehr Verantwortung vom Kunden und ermöglicht es uns, alle diese Builder-Sequenzaufrufe zu berücksichtigen und bei Bedarf wiederzuverwenden.
const promptBuilder = new PromptBuilder() const prompt1 = promptBuilder .buildStep1() // optional .buildStep2() // optional .buildStep3() // optional .build() // we've got a prompt const prompt2 = promptBuilder .buildStep1() // optional .buildStep3() // optional .build() // we've got a prompt
Kundencode
const promptBuilder = new PromptBuilder() const prompt1 = promptBuilder.buildStep1().buildStep2().build() const prompt2 = promptBuilder.buildStep1().buildStep3().build()Wie Sie dem obigen Code entnehmen können, muss der Clientcode die Details zum Erstellen von prompt1 oder prompt2 nicht kennen. Es ruft einfach den Director auf, legt das richtige Builder-Objekt fest und ruft dann die makePrompt-Methoden auf.
Praktisches Szenario
KI-CLI-Tool zur sofortigen Generierung von Engineering-Bildernvon Grund auf.
Der Quellcode für diese CLI-App ist hier verfügbar.Das CLI-Tool funktioniert wie folgt:
Datei: prompts.ts
class Director { private builder: PromptBuilder constructor() {} setBuilder(builder: PromptBuilder) { this.builder = builder } makePrompt1() { return this.builder.buildStep1().buildStep2().build() } makePrompt2() { return this.builder.buildStep1().buildStep3().build() } }
Datei: prompts.ts
const director = new Director() const builder = new PromptBuilder() director.setBuilder(builder) const prompt1 = director.makePrompt1() const prompt2 = director.makePrompt2()Wie Sie hier sehen können, erfordert jeder Eingabeaufforderungstyp die Erstellung vieler komplexer Attribute, wie z. B.
artStyle , colorPalette , lightingEffect , perspective , Kameratyp , usw.
Erkunden Sie gerne alle Attributdetails, die in der Dateienums.ts unseres Projekts definiert sind.
enums.ts
class PromptBuilder { private prompt: Prompt constructor() { this.reset() } reset() { this.prompt = new Prompt() } buildStep1() { this.prompt.subject = "A cheese eating a burger" //initialization code... return this } buildStep2() { //initialization code... return this } buildStep3() { //initialization code... return this } build() { const result = structuredClone(this.prompt) // deep clone this.reset() return result } }
Der Benutzer unserer CLI-App ist sich möglicherweise nicht aller dieser Konfigurationen bewusst. Vielleicht möchten sie einfach nur ein Bild basierend auf einem bestimmten Thema wie Käse essender Burger und einem bestimmten Stil (realistische oder digitale Kunst) erstellen.
Nachdem Sie das Github-Repository geklont haben, installieren Sie die Abhängigkeiten mit dem folgenden Befehl:
const promptBuilder = new PromptBuilder() const prompt1 = promptBuilder .buildStep1() // optional .buildStep2() // optional .buildStep3() // optional .build() // we've got a prompt const prompt2 = promptBuilder .buildStep1() // optional .buildStep3() // optional .build() // we've got a prompt
Führen Sie nach der Installation der Abhängigkeiten den folgenden Befehl aus:
const promptBuilder = new PromptBuilder() const prompt1 = promptBuilder.buildStep1().buildStep2().build() const prompt2 = promptBuilder.buildStep1().buildStep3().build()
Sie werden aufgefordert, einen Eingabeaufforderungstyp auszuwählen: Realistisch oder Digitale Kunst.
Dann müssen Sie den Betreff Ihrer Eingabeaufforderung eingeben. Bleiben wir beim Cheese-Eating-Burger.
Abhängig von Ihrer Auswahl erhalten Sie als Ergebnis die folgenden Textaufforderungen:
Eingabeaufforderung für einen realistischen Stil:
class Director { private builder: PromptBuilder constructor() {} setBuilder(builder: PromptBuilder) { this.builder = builder } makePrompt1() { return this.builder.buildStep1().buildStep2().build() } makePrompt2() { return this.builder.buildStep1().buildStep3().build() } }
Aufforderung zum digitalen Kunststil :
const director = new Director() const builder = new PromptBuilder() director.setBuilder(builder) const prompt1 = director.makePrompt1() const prompt2 = director.makePrompt2()
Kopieren Sie die vorherigen Befehle und fügen Sie sie dann in ChatGPT ein. ChatGPT verwendet das DALL-E 3-Modell, um die Bilder zu generieren.
Realistisches Ergebnis der Bildaufforderung
Eingabeaufforderungsergebnis für digitale Kunstbilder
Denken Sie an die Komplexität der Eingabeaufforderungsparameter und das Fachwissen, das zum Erstellen jeder Art von Eingabeaufforderung erforderlich ist, ganz zu schweigen von den hässlichen Konstruktoraufrufen, die erforderlich sind.
class RealisticPhotoPrompt { constructor( public subject: string, public location: string, public timeOfDay: string, public weather: string, public camera: CameraType, public lens: LensType, public focalLength: number, public aperture: string, public iso: number, public shutterSpeed: string, public lighting: LightingCondition, public composition: CompositionRule, public perspective: string, public foregroundElements: string[], public backgroundElements: string[], public colorScheme: ColorScheme, public resolution: ImageResolution, public postProcessing: string[] ) {} }
Haftungsausschluss: Dieser hässliche Konstruktoraufruf stellt in JavaScript kein großes Problem dar, da wir ein Konfigurationsobjekt übergeben können, bei dem alle Eigenschaften nullbar sind.
Um den Prozess der Eingabeaufforderungserstellung zu abstrahieren und unseren Code für Erweiterungen zu öffnen und für Änderungen zu schließen (O in SOLID) zu machen und die Verwendung unserer Eingabeaufforderungsgenerierungsbibliothek für unsere Bibliothekskunden nahtlos oder einfacher zu gestalten, Wir werden uns für die Implementierung des Builder-Designmusters entscheiden.
Beginnen wir mit der Deklaration der generischen Prompt Builder-Schnittstelle.
Die Schnittstelle deklariert eine Reihe von Methoden:
builders.ts
class PromptBuilder { private prompt: Prompt constructor() { this.reset() } reset() { this.prompt = new Prompt() } buildStep1() { this.prompt.subject = "A cheese eating a burger" //initialization code... return this } buildStep2() { //initialization code... return this } buildStep3() { //initialization code... return this } build() { const result = structuredClone(this.prompt) // deep clone this.reset() return result } }
builders.ts
const promptBuilder = new PromptBuilder() const prompt1 = promptBuilder .buildStep1() // optional .buildStep2() // optional .buildStep3() // optional .build() // we've got a prompt const prompt2 = promptBuilder .buildStep1() // optional .buildStep3() // optional .build() // we've got a prompt
Wie Sie aus den obigen Implementierungen ersehen können, entscheidet sich jeder Builder dafür, seine eigene Art von Eingabeaufforderung zu erstellen (die endgültigen Eingabeaufforderungsformen sind unterschiedlich) und sich dabei an dieselben Erstellungsschritte zu halten, die im PromptBuilder-Vertrag definiert sind!
Jetzt kommen wir zu unserer Klassendefinition Direktor.
director.ts
const promptBuilder = new PromptBuilder() const prompt1 = promptBuilder.buildStep1().buildStep2().build() const prompt2 = promptBuilder.buildStep1().buildStep3().build()
Die Klasse Director umschließt einen PromptBuilder und ermöglicht uns die Erstellung einer Eingabeaufforderungskonfiguration, die aus dem Aufruf aller Builder-Methoden von setSubject bis besteht buildArtisticElements.
Dadurch wird unser Client-Code in der Datei index.ts vereinfacht, was wir im nächsten Abschnitt sehen werden.
serializers.ts
class Director { private builder: PromptBuilder constructor() {} setBuilder(builder: PromptBuilder) { this.builder = builder } makePrompt1() { return this.builder.buildStep1().buildStep2().build() } makePrompt2() { return this.builder.buildStep1().buildStep3().build() } }
Um den endgültigen Eingabeaufforderungstext auf der Terminalkonsole zu drucken, habe ich einige Serialisierungsfunktionen für Dienstprogramme implementiert.
Jetzt ist unser Code zur sofortigen Bibliotheksgenerierung fertig. Nutzen wir es in der Datei index.ts.
index.ts
const director = new Director() const builder = new PromptBuilder() director.setBuilder(builder) const prompt1 = director.makePrompt1() const prompt2 = director.makePrompt2()
Der obige Code führt die folgenden Aktionen aus:
Denken Sie daran: Es ist nicht möglich, die Eingabeaufforderung vom Direktor zu erhalten, da die Form der Eingabeaufforderung von jedem Builder-Typ unterschiedlich ist.
Das Builder-Entwurfsmuster erweist sich als hervorragende Lösung für die Erstellung komplexer Objekte mit mehreren Konfigurationen, wie unsere CLI-Anwendung zur Generierung von KI-Bildaufforderungen zeigt. Hier erfahren Sie, warum das Builder-Muster in diesem Szenario von Vorteil war:
Vereinfachte Objekterstellung: Das Muster ermöglichte es uns, komplizierte RealisticPhotoPrompt- und DigitalArtPrompt-Objekte zu erstellen, ohne ihren komplexen Konstruktionsprozess dem Client-Code auszusetzen.
Flexibilität: Durch die Verwendung separater Builder-Klassen für jeden Eingabeaufforderungstyp können wir problemlos neue Eingabeaufforderungstypen hinzufügen oder vorhandene ändern, ohne den Client-Code zu ändern.
Code-Organisation: Das Muster trug dazu bei, die Konstruktionslogik von der Darstellung zu trennen, wodurch der Code modularer und einfacher zu warten war.
Wiederverwendbarkeit: Die PromptDirector-Klasse ermöglichte es uns, denselben Konstruktionsprozess für verschiedene Arten von Eingabeaufforderungen wiederzuverwenden und so die Wiederverwendbarkeit des Codes zu verbessern.
Abstraktion: Der Client-Code in index.ts blieb einfach und konzentrierte sich auf Logik auf hoher Ebene, während die Komplexität der Eingabeaufforderungskonstruktion in den Builder-Klassen abstrahiert wurde.
Wenn Sie Fragen haben oder etwas weiter besprechen möchten, können Sie mich gerne hier kontaktieren.
Viel Spaß beim Codieren!
Das obige ist der detaillierte Inhalt vonBeherrschen des Builder-Musters: Erstellen Sie eine dynamische AI-Prompt-Generator-CLI. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!