Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
IT Security NachrichtenAb 1.10: Meldepflicht für IT-Vorfälle in Österreich | heise online(22.09.2026 um 23:31 Uhr)
IT Security NachrichtenIT Security News Daily Summary 2026-09-22(22.09.2026 um 23:55 Uhr)
IT Security NachrichtenIT Security News Hourly Summary 2026-09-23 00h : 3 posts(23.09.2026 um 00:00 Uhr)
IT Security NachrichtenRelays Are Masking Chinese Access to Frontier AI Models in the US(22.09.2026 um 23:12 Uhr)
Malware / Trojaner / VirenAttackers Use Wallpaper Engine to Distribute Malware(22.09.2026 um 15:10 Uhr)
IT Security NachrichtenSweden fines Miljödata $183,000 over breach affecting 2.2 million(22.09.2026 um 23:40 Uhr)
IT Security NachrichtenRogue external MFA providers can steal passwords during logins(22.09.2026 um 23:45 Uhr)
IT Security NachrichtenÖsterreich setzt NIS2 um, erhält Bundesamt für Cybersicherheit(22.09.2026 um 23:24 Uhr)
IT NachrichtenHere’s a Newer Galaxy Z Fold 8 Update for You(22.09.2026 um 23:19 Uhr)
IT Security NachrichtenAb 1.10: Meldepflicht für IT-Vorfälle in Österreich | heise online(22.09.2026 um 23:31 Uhr)
IT Security NachrichtenIT Security News Daily Summary 2026-09-22(22.09.2026 um 23:55 Uhr)
IT Security NachrichtenIT Security News Hourly Summary 2026-09-23 00h : 3 posts(23.09.2026 um 00:00 Uhr)
IT Security NachrichtenRelays Are Masking Chinese Access to Frontier AI Models in the US(22.09.2026 um 23:12 Uhr)
Malware / Trojaner / VirenAttackers Use Wallpaper Engine to Distribute Malware(22.09.2026 um 15:10 Uhr)
IT Security NachrichtenSweden fines Miljödata $183,000 over breach affecting 2.2 million(22.09.2026 um 23:40 Uhr)
IT Security NachrichtenRogue external MFA providers can steal passwords during logins(22.09.2026 um 23:45 Uhr)
IT Security NachrichtenÖsterreich setzt NIS2 um, erhält Bundesamt für Cybersicherheit(22.09.2026 um 23:24 Uhr)
IT NachrichtenHere’s a Newer Galaxy Z Fold 8 Update for You(22.09.2026 um 23:19 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Mastering the Abstract Factory Pattern: A Comprehensive Guide

Have you ever found yourself needing to create multiple variations of different families of object in your application without duplicating the logic over and over? Or perhaps you’ve built an application, only to realize that new r…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Have you ever found yourself needing to create multiple variations of different families of object in your application without duplicating the logic over and over?



Or perhaps you’ve built an application, only to realize that new requirements or a client’s changed preferences demand entirely new objects, forcing you to rework your entire codebase?



What if there was a way to seamlessly introduce new variations without breaking your existing code just by plugging in a new implementation?



That’s where the Abstract Factory design pattern comes in!



In this tutorial, we’ll break down this powerful design pattern by building a Node.js CLI application for creating mutiple types of resumes supporting multiples formats and themes.






Overview



The Abstract Factory is a creational design pattern , which is a category of design patterns that deals with the different problems that come with the native way of creating objects using the new keyword or operator.




You can think of the Abstract Factory design pattern as a generalization of the factory method design pattern which we've covered in this blog article.







Problem



The Abstract Factory design pattern solves the following problems:




  1. How can we create families of related products such as: PDFResume , JSONResume , and MarkdownResume?

  2. How can we support having multiple variants per product family such as: CreativeResume , MinimalistResume , and ModernResume?

  3. How can we support adding more variants and products without breaking our existing consuming or client code?






Solution



The Abstract Factory design pattern solves these problems by declaring an interface or abstract class for each type of product.




export abstract class PDFResume {}
export abstract class JSONResume {}
export abstract class MarkdownResume {}







And then, as the name of the pattern implies, we create an abstract factory which is an interface that declares factory methods that create every type of product:





  • createPDFResume : which returns a PDFResume type or subtype.


  • createMarkdownResume : which returns a MarkdownResume type or subtype.


  • createJSONResume : which returns a JSONResume type or subtype.




export interface ResumeFactory {
createPDFResume(): PDFResume
createMarkdownResume(): MarkdownResume
createJSONResume(): JSONResume
}







Okay, now we have a generic factory which returns every possible type of product, but how can we support multiple variants per product?



The answer is by creating a ConcreteFactory which implements the abstract factory ( ResumeFactory ).




export class CreativeResumeFactory implements ResumeFactory {
createPDFResume(): CreativePDFResume {
return new CreativePDFResume() // CreativePDFResume implements PDFResume
}

createMarkdownResume(): CreativeMarkdownResume {
return new CreativeMarkdownResume() // CreativeMarkdownResume implements MarkdownResume
}

createJSONResume(): CreativeJSONResume {
return new CreativeJSONResume() // CreativeJSONResume implements JSONResume
}
}







Now, to consume our factories in our client class, we just have to declare a variable of type ResumeFactory and then instantiate the corresponding Concrete factory depending on the user input.




Client code:





// User inputs...
let theme = "minimalist"
let format = "pdf"

let factory: ResumeFactory

switch (theme) {
case "minimalist":
factory = new MinimalistResumeFactory()
break
case "modern":
factory = new ModernResumeFactory()
break
case "creative":
factory = new CreativeResumeFactory()
break
default:
throw new Error("Invalid theme.")
}

const userInput = await getUserInput()
let resume

switch (format) {
case "pdf":
resume = factory.createPDFResume()
break
case "markdown":
resume = factory.createMarkdownResume()
break
case "json":
resume = factory.createJSONResume()
break
default:
throw new Error("Invalid format.")
}










Structure



Abstract Factory Design Pattern Structure



The structure of the Abstract Factory design pattern consists of the following classes:





  1. Factory : The reason for naming this design pattern abstract factory is that this class represents the contract between all the ConcreteFactories. It defines all the factory methods.




  • The number of factory methods is equal to the number of products.

  • Each factory method should return an abstract or generic product type ( IProduct{j} ).




In our case, the factory methods declared in Factory are: createProductA and createProductB






  1. ConcreteFactory{i} : These classes implement the Factory class and provide custom implementations for each factory method.




  • In the above schema, i is equal to either 1 or 2.

  • The number of ConcreteFactories is equal to the number of possible variants per product.

  • Each concrete factory method should return an object which is an instance of the corresponding product.





  1. IProduct{j} : These classes correspond to the abstract product types.




  • In the above schema, j is equal to either A or B.

  • Each IProduct{j} is implemented by many concrete product classes.




ConcretProductA1 and ConcretProductA2 implement IProductA ConcretProductB1 and ConcretProductB2 implement IProductB






  1. ConcreteProducts are the products which implement one of the IProduct{j} generic types.






Practical Scenario



In this section, we are going to put the previous example into action by building a fully working Node.js TypeScript CLI Application which creates a resume based on the chosen theme and format by the user.



Feel free to check out the full working code by cloning this repository on your machine.



Then run the following commands:




npm install
npm start










Declaring Types



Let's start by declaring the types which we will be using throughout the tutorial to ensure type safety.




interfaces/Types





export type ResumeData = {
name: string
email: string
phone: string
experience: Experience[]
}

export type Experience = {
company: string
position: string
startDate: string
endDate: string
description: string
}








  1. The ResumeData type defines all the attributes of a resume object such as: name, email, phone, and an array of experiences.

  2. The Experience type consists of the: company, position, startDate, endDate, and description.






Declaring Our Abstract Factory



Now, let's declare the generic factory type, which will be defining the three factory methods which correspond to the different supported product types: PDFResume , MarkdownResume , and JSONResume.




interfaces/ResumeFactory





import { JSONResume } from "../resumes/json/JSONResume"
import { MarkdownResume } from "../resumes/markdown/MarkdownResume"
import { PDFResume } from "../resumes/pdf/PdfResume"

export interface ResumeFactory {
createPDFResume(): PDFResume
createMarkdownResume(): MarkdownResume
createJSONResume(): JSONResume
}








We will be going through their code in the next section.







Declaring The Shared Class for the Different Types of Documents



Next, let's move on to creating our generic product classes.



Every product type will be an abstract class because we want to share both attributes and methods between their corresponding subtypes.





  1. JSONResume : The class has a protected data attribute, storing an object of type ResumeData with an extra attribute called style.



The class defines:




  • A getter method to access the data attribute.

  • An abstract generate method which will be overridden by the subclasses later.

  • A saveToFile method with a basic implementation, which consists of storing the resume data in a JSON file.




resumes/json/JSONResume





import * as fs from "fs/promises"

import { ResumeData } from "../../interfaces/Types"

export abstract class JSONResume {
protected data!: ResumeData & { style: string }

abstract generate(data: ResumeData): void

async saveToFile(fileName: string): Promise<void> {
await fs.writeFile(fileName, JSON.stringify(this.data, null, 2))
}

getData(): any {
return this.data
}
}








The keyword abstract means that the class is a generic type which can't be instantiated; it can only be inherited by other classes.






  1. MarkdownResume : The class has a protected content attribute, storing the markdown string.



The class defines:




  • A getter method to access the content attribute.

  • An abstract generate method which will be overridden by the subclasses later.

  • A saveToFile method which takes a fileName and then stores the markdown formatted string content into a file.




resumes/markdown/MarkdownResume





import * as fs from "fs/promises"

import { ResumeData } from "../../interfaces/Types"

export abstract class MarkdownResume {
protected content: string = ""

abstract generate(data: ResumeData): void

async saveToFile(fileName: string): Promise<void> {
await fs.writeFile(fileName, this.content)
}

getContent(): string {
return this.content
}
}









  1. PDFResume :



The class has a protected doc object of type PDFKit.PDFDocument , which is imported from a library called pdfkit. The library simplifies creating and manipulating PDF documents through its object-oriented interface.



The class defines:




  • A getter method to access the doc attribute.

  • An abstract generate method which will be overridden by the subclasses later.

  • A saveToFile method which saves the doc in-memory PDF object into a specific file.




resumes/pdf/PDFResume





import * as fs from "fs"

import PDFDocument from "pdfkit"

import { ResumeData } from "../../interfaces/Types"

export abstract class PDFResume {
protected doc: PDFKit.PDFDocument

constructor() {
this.doc = new PDFDocument()
}

abstract generate(data: ResumeData): void

async saveToFile(fileName: string): Promise<void> {
const stream = fs.createWriteStream(fileName)
this.doc.pipe(stream)
this.doc.end()

await new Promise<void>((resolve, reject) => {
stream.on("finish", resolve)
stream.on("error", reject)
})
}

getBuffer(): Buffer {
return this.doc.read() as Buffer
}
}










Declaring our Concrete Factories



Now that we've defined our generic product types and our abstract factory , it's time to proceed with the creation of our ConcreteFactories which correspond to the different variants of every generic product type.



We have 3 possible variants for a resume: Creative , Minimalist , and Modern. And 3 types of generic Products: JSON , PDF , and Markdown.



The abstract factory ( ResumeFactory ) defines the 3 factory methods which are responsible for creating our products:





  • createPDFResume : creates an instance of type PDFResume.


  • createMarkdownResume : creates an instance of type MarkdownResume.


  • createJSONResume : creates an instance of type JSONResume.



To support multiple variants per product, we will have to create 3 concrete factories.



Each Concrete factory will be creating the 3 types of products but with its own flavors:





  1. CreativeResumeFactory creates products of the Creative variant.


  2. MinimalistResumeFactory creates products of the Minimalist variant.


  3. ModernResumeFactory creates products of the Modern variant.




factories/CreativeResumeFactory





import { ResumeFactory } from "../interfaces/ResumeFactory"
import { CreativeJSONResume } from "../resumes/json/CreativeJSONResume"
import { CreativeMarkdownResume } from "../resumes/markdown/CreativeMarkdownResume"
import { CreativePDFResume } from "../resumes/pdf/CreativePDFResume"

export class CreativeResumeFactory implements ResumeFactory {
createPDFResume(): CreativePDFResume {
return new CreativePDFResume() // CreativePDFResume extends PDFResume
}

createMarkdownResume(): CreativeMarkdownResume {
return new CreativeMarkdownResume() // CreativeMarkdownResume extends MarkdownResume
}

createJSONResume(): CreativeJSONResume {
return new CreativeJSONResume() // CreativeJSONResume extends JSONResume
}
}








  • The CreativeResumeFactory factory methods return the creative concrete product variant for every type of product.




factories/MinimalistResumeFactory





import { ResumeFactory } from "../interfaces/ResumeFactory"
import { MinimalistJSONResume } from "../resumes/json/MinimalistJSONResume"
import { MinimalistMarkdownResume } from "../resumes/markdown/MinimalistMarkdownResume"
import { MinimalistPDFResume } from "../resumes/pdf/MinimalistPDFResume"

export class MinimalistResumeFactory implements ResumeFactory {
createPDFResume(): MinimalistPDFResume {
return new MinimalistPDFResume() // extends PDFResume
}

createMarkdownResume(): MinimalistMarkdownResume {
return new MinimalistMarkdownResume() // extends MarkdownResume
}

createJSONResume(): MinimalistJSONResume {
return new MinimalistJSONResume() // extends JSONResume
}
}








  • The MinimalistResumeFactory factory methods return the minimalist concrete product variant for every type of product.




factories/ModernResumeFactory





import { ResumeFactory } from "../interfaces/ResumeFactory"
import { ModernJSONResume } from "../resumes/json/ModernJSONResume"
import { ModernMarkdownResume } from "../resumes/markdown/ModernMarkdownResume"
import { ModernPDFResume } from "../resumes/pdf/ModernPDFResume"

export class ModernResumeFactory implements ResumeFactory {
createPDFResume(): ModernPDFResume {
return new ModernPDFResume() // extends PDFResume
}

createMarkdownResume(): ModernMarkdownResume {
return new ModernMarkdownResume() // extends MarkdownResume
}

createJSONResume(): ModernJSONResume {
return new ModernJSONResume() // extends JSONResume
}
}








  • The ModernResumeFactory factory methods return the modern concrete product variant for every type of product.






The Creative Resume Factory Concrete Products



Now, let's create the previous ConcreteProducts which are returned by the CreativeResumeFactory



PDF Resume :




resumes/pdf/CreativePDFResume





import { ResumeData } from "../../interfaces/Types"
import { PDFResume } from "./PdfResume"

export class CreativePDFResume extends PDFResume {
generate(data: ResumeData): void {
this.doc.rect(0, 0, 200, this.doc.page.height).fill("#FFD700")
this.doc.fill("black").fontSize(28).text(data.name, 220, 50)
this.doc.fontSize(12).text(`\${data.email} | \${data.phone}`, 220, 80)
this.doc.fontSize(16).text("Experience", 220, 120)
let yPos = 140
data.experience.forEach((exp: any) => {
this.doc.fontSize(14).text(exp.company, 220, yPos)
this.doc
.fontSize(12)
.text(
`\${exp.position} (\${exp.startDate} - \${exp.endDate})`,
220,
yPos + 20
)
this.doc
.fontSize(10)
.text(exp.description, 220, yPos + 40, { width: 350 })
yPos += 80
})
}
}







Markdown Resume :




resumes/markdown/CreativeMarkdownResume





import { ResumeData } from "../../interfaces/Types"
import { MarkdownResume } from "./MarkdownResume"

export class CreativeMarkdownResume extends MarkdownResume {
generate(data: ResumeData): void {
this.content = `# 🌟 \${data.name} 🌟\n\n`
this.content += `📧 \${data.email} | 📞 \${data.phone}\n\n`
this.content += "## 💼 Career Journey\n\n"
data.experience.forEach((exp: any) => {
this.content += `### 🏢 \${exp.company}\n\n`
this.content += ` **\${exp.position}** | \${exp.startDate} - \${exp.endDate}\n\n`
this.content += `\${exp.description}\n\n`
})
}
}







JSON Resume :




resumes/json/CreativeJSONResume





import { ResumeData } from "../../interfaces/Types"
import { JSONResume } from "./JSONResume"

export class CreativeJSONResume extends JSONResume {
generate(data: ResumeData): void {
this.data = {
style: "creative",
...data,
}
}
}










The Minimalist Resume Factory Concrete Products



Next, let's create the previous ConcreteProducts which are returned by the MinimalistResumeFactory



PDF Resume :




resumes/pdf/MinimalistPDFResume





import { ResumeData } from "../../interfaces/Types"
import { PDFResume } from "./PdfResume"

export class MinimalistPDFResume extends PDFResume {
generate(data: ResumeData): void {
this.doc.fontSize(24).text(data.name, { align: "center" })
this.doc
.fontSize(12)
.text(`Email: \${data.email} | Phone: \${data.phone}`, {
align: "center",
})
this.doc.moveDown()
this.doc.fontSize(16).text("Experience")
data.experience.forEach((exp: any) => {
this.doc.fontSize(14).text(exp.company)
this.doc
.fontSize(12)
.text(`\${exp.position} (\${exp.startDate} - \${exp.endDate})`)
this.doc.fontSize(10).text(exp.description)
this.doc.moveDown()
})
}
}







Markdown Resume :




resumes/markdown/MinimalistMarkdownResume





import { ResumeData } from "../../interfaces/Types"
import { MarkdownResume } from "./MarkdownResume"

export class MinimalistMarkdownResume extends MarkdownResume {
generate(data: ResumeData): void {
this.content = `# \${data.name}\n\n`
this.content += `Email: \${data.email} | Phone: \${data.phone}\n\n`
this.content += "## Experience\n\n"
data.experience.forEach((exp: any) => {
this.content += `### \${exp.company}\n\n`
this.content += `\${exp.position} (\${exp.startDate} - \${exp.endDate})\n\n`
this.content += `\${exp.description}\n\n`
})
}
}







JSON Resume :




resumes/json/MinimalistJSONResume





import { ResumeData } from "../../interfaces/Types"
import { JSONResume } from "./JSONResume"

export class MinimalistJSONResume extends JSONResume {
generate(data: ResumeData): void {
this.data = {
style: "minimalist",
...data,
}
}
}










The Modern Resume Factory Concrete Products



Finally, let's create the previous ConcreteProducts which are returned by the ModernResumeFactory



PDF Resume :




resumes/pdf/ModernPDFResume





import { ResumeData } from "../../interfaces/Types"
import { PDFResume } from "./PdfResume"

export class ModernPDFResume extends PDFResume {
generate(data: ResumeData): void {
this.doc.rect(0, 0, this.doc.page.width, 60).fill("#4A90E2")
this.doc.fill("white").fontSize(28).text(data.name, 50, 20)
this.doc.fontSize(12).text(`\${data.email} | \${data.phone}`, 50, 50)
this.doc.fill("black").fontSize(16).text("Experience", 50, 80)
let yPos = 100
data.experience.forEach((exp: any) => {
this.doc.fontSize(14).text(exp.company, 50, yPos)
this.doc
.fontSize(12)
.text(
`\${exp.position} (\${exp.startDate} - \${exp.endDate})`,
50,
yPos + 20
)
this.doc.fontSize(10).text(exp.description, 50, yPos + 40, { width: 500 })
yPos += 80
})
}
}







Markdown Resume :




resumes/markdown/ModernMarkdownResume





import { ResumeData } from "../../interfaces/Types"
import { MarkdownResume } from "./MarkdownResume"

export class ModernMarkdownResume extends MarkdownResume {
generate(data: ResumeData): void {
this.content = `# \${data.name}\n\n`
this.content += ` **Email:** \${data.email} | **Phone:** \${data.phone}\n\n`
this.content += "## Professional Experience\n\n"
data.experience.forEach((exp: any) => {
this.content += `### \${exp.company}\n\n`
this.content += ` **\${exp.position}** | \${exp.startDate} - \${exp.endDate}\n\n`
this.content += `\${exp.description}\n\n`
})
}
}







JSON Resume :




resumes/json/ModernJSONResume





import { ResumeData } from "../../interfaces/Types"
import { JSONResume } from "./JSONResume"

export class ModernJSONResume extends JSONResume {
generate(data: ResumeData): void {
this.data = {
style: "modern",
...data,
}
}
}










Using Our Factories in our Index.ts File



Let's start bearing the fruits of our previous work by using our factories in the client code.



Look how we can now consume our resume builder library in a very clean way by just using our factories.



The user only has to provide two things:





  1. The Product Type : What type of PDFs does he want to create?


  2. The theme : What kind of resume styles does he prefer?




index.ts





#!/usr/bin/env node
import chalk from "chalk"
import inquirer from "inquirer"

import { CreativeResumeFactory } from "./factories/CreativeResumeFactory"
import { MinimalistResumeFactory } from "./factories/MinimalistResumeFactory"
import { ModernResumeFactory } from "./factories/ModernResumeFactory"
import { ResumeFactory } from "./interfaces/ResumeFactory"
import { getUserInput } from "./utils/userInput"

async function main() {
console.log(chalk.blue.bold("Welcome to the Theme-Based Resume Generator!"))

const { theme, format } = await inquirer.prompt([
{
type: "list",
name: "theme",
message: "Choose a resume theme:",
choices: ["minimalist", "modern", "creative"],
},
{
type: "list",
name: "format",
message: "Choose an output format:",
choices: ["pdf", "markdown", "json"],
},
])

let factory: ResumeFactory

switch (theme) {
case "minimalist":
factory = new MinimalistResumeFactory()
break
case "modern":
factory = new ModernResumeFactory()
break
case "creative":
factory = new CreativeResumeFactory()
break
default:
throw new Error("Invalid theme.")
}

const userInput = await getUserInput()
let resume

switch (format) {
case "pdf":
resume = factory.createPDFResume()
break
case "markdown":
resume = factory.createMarkdownResume()
break
case "json":
resume = factory.createJSONResume()
break
default:
throw new Error("Invalid format.")
}

console.log(chalk.yellow("Generating your resume..."))

try {
resume.generate(userInput)
const fileName = `\${userInput.name.replace(/\s+/g, "_")}_\${theme}_resume.\${format}`
await resume.saveToFile(fileName)
console.log(chalk.green.bold("Resume generated successfully!"))
console.log(
chalk.cyan(
`Your \${theme} \${format} resume has been saved as \${fileName}.`
)
)
} catch (error) {
console.error(
chalk.red("Error generating resume:"),
(error as Error).message
)
}
}

main().catch((error) => {
console.error(chalk.red("An unexpected error occurred:"), error.message)
process.exit(1)
})







The code above works in three steps:





  1. User Inputs: We first get the theme and format values.


  2. Choosing A factory : Then we instantiate the corresponding factory based on the theme value.


  3. Creating the Product : Finally, we call the corresponding factory method depending on the chosen format.



The user doesn't care about how products and their corresponding variants are created; they only need to select a theme and format , and that's it - the corresponding product gets created as requested.



The client code is now robust for changes. If we want to add a new theme or style, we can just create a new factory which is responsible for doing so.




We've used the chalk library to color our terminal logs depending on their semantic meaning.




To be able to get the inputs from the CLI app's user, we've used the inquirer package, which provides a really appealing and user-friendly way to get various types of inputs from the user.




  1. The getUserInput function was used to get the main resume information: name, email, phone.

  2. The getExperience utility function was used to recursively retrieve the experience information from the user. In other words, it prompts the user to fill in the experience information for the first entry, then asks if they have another experience to add. If the answer is no, the function just returns; on the other hand, if they select yes, they will be asked again to fill in the next experience's information.




utils/userInput





import inquirer from "inquirer"

import { Experience, ResumeData } from "../interfaces/Types"

export async function getUserInput(): Promise<ResumeData> {
const questions = [
{
type: "input",
name: "name",
message: "Enter your full name:",
validate: (input: string) => input.trim() !== "" || "Name is required",
},
{
type: "input",
name: "email",
message: "Enter your email address:",
validate: (input: string) =>
/\S+@\S+\.\S+/.test(input) || "Please enter a valid email address",
},
{
type: "input",
name: "phone",
message: "Enter your phone number:",
validate: (input: string) =>
input.trim() !== "" || "Phone number is required",
},
]

const { name, email, phone } = await inquirer.prompt<{
name: string
email: string
phone: string
}>(questions)

const experience = await getExperience()

return { name, email, phone, experience }
}

async function getExperience(): Promise<Experience[]> {
const experience: Experience[] = []
let addMore = true

while (addMore) {
const job = await inquirer.prompt<Experience>([
{
type: "input",
name: "company",
message: "Enter company name:",
validate: (input: string) =>
input.trim() !== "" || "Company name is required",
},
{
type: "input",
name: "position",
message: "Enter your position:",
validate: (input: string) =>
input.trim() !== "" || "Position is required",
},
{
type: "input",
name: "startDate",
message: "Enter start date (MM/YYYY):",
validate: (input: string) =>
/^\d{2}\/\d{4}$/.test(input) || "Please enter a valid date (MM/YYYY)",
},
{
type: "input",
name: "endDate",
message: 'Enter end date (MM/YYYY or "Present"):',
validate: (input: string) =>
/^\d{2}\/\d{4}$/.test(input) ||
input.toLowerCase() === "present" ||
'Please enter a valid date (MM/YYYY) or "Present"',
},
{
type: "input",
name: "description",
message: "Enter job description:",
validate: (input: string) =>
input.trim() !== "" || "Job description is required",
},
])

experience.push(job)

const { continueAdding } = await inquirer.prompt<{
continueAdding: boolean
}>({
type: "confirm",
name: "continueAdding",
message: "Do you want to add another job experience?",
default: false,
})

addMore = continueAdding
}

return experience
}










Conclusion



The Abstract Factory pattern is a powerful tool in the arsenal of software designers and developers. It provides a structured approach to creating families of related objects without specifying their concrete classes. This pattern is particularly useful when:




  1. A system should be independent of how its products are created, composed, and represented.

  2. A system needs to be configured with one of multiple families of products.

  3. A family of related product objects is designed to be used together, and you need to enforce this constraint.

  4. You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations.



In our practical example, we've seen how the Abstract Factory pattern can be applied to create a flexible and extensible resume generation system. This system can easily accommodate new resume styles or output formats without modifying the existing code, demonstrating the power of the Open/Closed Principle in action.



While the Abstract Factory pattern offers many benefits, it's important to note that it can introduce additional complexity to your codebase. Therefore, it's crucial to assess whether the flexibility it provides is necessary for your specific use case.



By mastering design patterns like the Abstract Factory, you'll be better equipped to create robust, flexible, and maintainable software systems. Keep exploring and applying these patterns in your projects to elevate your software design skills.






Contact



If you have any questions or want to discuss something further, feel free to Contact me here.



Happy coding!

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Mastering the Abstract Factory Pattern: A Comprehensive Guide

Thematisch verwandte Begriffe: Mastering, Abstract, Factory, Pattern · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-58268 | SIPGO is a library for writing SIP services in the GO language. Prior to…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick