Skip to content

Instantly share code, notes, and snippets.

@ThatXliner
Created July 11, 2024 03:11
Show Gist options
  • Save ThatXliner/c114dab9499785632f195b93140bb580 to your computer and use it in GitHub Desktop.
Save ThatXliner/c114dab9499785632f195b93140bb580 to your computer and use it in GitHub Desktop.
OOP Design Patterns
// Adapter pattern example for integrating different payment gateways
class PayPal {
makePayment(amount) {
console.log(`Paying ${amount} via PayPal`);
}
}
class Stripe {
pay(amount) {
console.log(`Paying ${amount} via Stripe`);
}
}
// Adapter for Stripe to conform to PayPal interface
class StripeAdapter {
constructor(stripe) {
this.stripe = stripe;
}
makePayment(amount) {
this.stripe.pay(amount);
}
}
// Usage
const paypal = new PayPal();
const stripe = new Stripe();
const stripeAdapter = new StripeAdapter(stripe);
paypal.makePayment(100); // Paying 100 via PayPal
stripeAdapter.makePayment(200); // Paying 200 via Stripe
// Builder pattern example for creating a complex object step by step
// This example may seem like a toy (it is)
// but a real-life example (which I use in FRC) of a Builder pattern API
// can be found here (note that it's in Java):
// https://api.ctr-electronics.com/phoenix6/release/java/com/ctre/phoenix6/configs/Slot0Configs.html
// (for the above example, take note of the `with*` methods such as `.withKV`)
class Product {
constructor() {
this.parts = [];
}
addPart(part) {
this.parts.push(part);
}
showParts() {
console.log('Product parts:', this.parts.join(', '));
}
}
class ProductBuilder {
constructor() {
this.product = new Product();
}
buildPartA() {
this.product.addPart('Part A');
return this;
}
buildPartB() {
this.product.addPart('Part B');
return this;
}
getProduct() {
return this.product;
}
}
// Usage
const builder = new ProductBuilder();
const product = builder.buildPartA().buildPartB().getProduct();
product.showParts(); // Product parts: Part A, Part B
// Decorator pattern example for adding toppings to a pizza
class Pizza {
getDescription() {
return 'Plain pizza';
}
cost() {
return 10;
}
}
class TomatoSauceDecorator {
constructor(pizza) {
this.pizza = pizza;
}
getDescription() {
return this.pizza.getDescription() + ' with tomato sauce';
}
cost() {
return this.pizza.cost() + 2;
}
}
class CheeseDecorator {
constructor(pizza) {
this.pizza = pizza;
}
getDescription() {
return this.pizza.getDescription() + ' with cheese';
}
cost() {
return this.pizza.cost() + 3;
}
}
// Usage
let pizza = new Pizza();
console.log(pizza.getDescription(), 'Cost:', pizza.cost());
pizza = new TomatoSauceDecorator(pizza);
console.log(pizza.getDescription(), 'Cost:', pizza.cost());
pizza = new CheeseDecorator(pizza);
console.log(pizza.getDescription(), 'Cost:', pizza.cost());
// Output:
// Plain pizza Cost: 10
// Plain pizza with tomato sauce Cost: 12
// Plain pizza with tomato sauce with cheese Cost: 15
// Factory pattern example for creating different shapes
class ShapeFactory {
createShape(type) {
switch (type) {
case 'circle':
return new Circle();
case 'rectangle':
return new Rectangle();
default:
throw new Error('Invalid shape type');
}
}
}
class Circle {
draw() {
console.log('Drawing a circle');
}
}
class Rectangle {
draw() {
console.log('Drawing a rectangle');
}
}
// Usage
const factory = new ShapeFactory();
const circle = factory.createShape('circle');
const rectangle = factory.createShape('rectangle');
circle.draw(); // Drawing a circle
rectangle.draw(); // Drawing a rectangle
// Proxy pattern example for controlling access to a sensitive object
class SensitiveObject {
constructor(data) {
this.data = data
}
access() {
console.log('Accessing sensitive object');
return this.data;
}
}
class ProxyObject {
constructor(sensitiveObject) {
this.sensitiveObject = sensitiveObject;
}
access() {
if (this.checkAccess()) {
return this.sensitiveObject.access();
} else {
console.log('Access denied');
}
}
checkAccess() {
// Perform validation or checks here
return false; // Simulating access denial for demonstration
}
}
// Usage
const sensitiveObject = new SensitiveObject('sensitive data');
const proxy = new ProxyObject(sensitiveObject);
proxy.access(); // Access denied
// Proxy pattern example for controlling access to a sensitive object
// but taking advantage of JavaScript's Proxy class
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
// Exact same class as before
class SensitiveObject {
constructor(data) {
this.data = data;
}
access() {
console.log('Accessing sensitive object');
return this.data;
}
}
// Proxy handler for access control
const handler = {
get(target, property) {
if (property === 'data' && !this.checkAccess()) {
console.log('Access denied');
return undefined;
}
if (property === 'access') {
if (this.checkAccess()) {
return target[property].bind(target);
} else {
return () => console.log('Access denied');
}
}
console.log(`Accessing property '${property}'`);
return target[property];
},
checkAccess() {
// Perform validation or checks here
return false; // Simulating access denial for demonstration
}
};
// Creating a proxy object
const sensitiveObject = new SensitiveObject('sensitive data');
const proxy = new Proxy(sensitiveObject, handler);
// Using the proxy object
console.log(proxy.data); // Access denied, undefined
proxy.access(); // Access denied
// Modifying the checkAccess method to grant access
handler.checkAccess = function() {
return true; // Granting access for demonstration
};
console.log(proxy.data); // Accessing property 'data', Sensitive data
proxy.access(); // Accessing sensitive object
// Singleton example for a Configuration Manager
class ConfigurationManager {
constructor() {
if (ConfigurationManager.instance) {
return ConfigurationManager.instance;
}
// Initialize configuration here
this.config = {
language: 'en',
theme: 'light'
};
ConfigurationManager.instance = this;
return this;
}
getConfig() {
return this.config;
}
setConfig(config) {
this.config = config;
}
}
// Usage
const configManager1 = new ConfigurationManager();
const configManager2 = new ConfigurationManager();
console.log(configManager1 === configManager2); // true (same instance)
configManager1.setConfig({ language: 'fr', theme: 'dark' });
console.log(configManager2.getConfig()); // { language: 'fr', theme: 'dark' }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment