05 - Observer and Strategy Pattern: Flexible Behavior | Federico Calò
05 - Observer and Strategy Pattern: Flexible Behavior | Federico Calò
Skip to main content Home Hi! I'm
I create modern web applications and custom digital tools to help businesses grow through technological innovation. My passion is combining computer science and economics to generate real value.
My passion for computer science was born at the Technical Commercial Institute of Maglie , where I discovered the power of programming and the fascination of creating digital solutions. From the start, I understood that computer science was not just code, but an extraordinary tool for turning ideas into reality.
During my studies in Business Information Systems , I began to interweave computer science and economics, understanding how technology can be the engine of growth for any business. This vision accompanied me to the University of Bari , where I obtained my degree in Computer Science, deepening my technical skills and passion for software development.
Today I put this experience at the service of businesses, professionals and startups, creating tailor-made digital solutions that automate processes, optimize resources and open new business opportunities. Because true innovation begins when technology meets the real needs of people.
Credo fermamente che l'informatica sia lo strumento più potente per trasformare le idee in realtà e migliorare la vita delle persone.
Democratizzare la Tecnologia La mia missione è rendere l'informatica accessibile a tutti : dalle piccole imprese locali alle startup innovative, fino ai professionisti che vogliono digitalizzare la propria attività. Ogni realtà merita di sfruttare le potenzialità del digitale.
Unire Informatica ed Economia Non è solo questione di scrivere codice: è capire come la tecnologia possa generare valore reale . Intrecciando competenze informatiche e visione economica, aiuto le attività a crescere, ottimizzare processi e raggiungere nuovi traguardi di efficienza e redditività.
Creare Soluzioni su Misura Ogni attività è unica, e così devono esserlo le soluzioni. Sviluppo strumenti personalizzati che rispondono alle esigenze specifiche di ciascun cliente, automatizzando processi ripetitivi e liberando tempo per ciò che conta davvero: far crescere il business.
Trasforma la Tua Attività con la Tecnologia Che tu gestisca un negozio, uno studio professionale o un'azienda, posso aiutarti a sfruttare le potenzialità dell'informatica per lavorare meglio, più velocemente e in modo più intelligente.
Parliamone Insieme → All 8 Anthropic 5 Connect 1 Oracle 1 RoadMap.sh 1 Sort by: 📅 Date (most recent) 🔤 Name A-Z 🏢 Organization
Reinvention With Agentic AI Learning Program Anthropic
December 2024 Agentic AI Fluency Anthropic
December 2024 AI Fluency for Students Anthropic
December 2024 AI Fluency: Framework and Foundations Anthropic
December 2024 Claude with the Anthropic API Anthropic
December 2024 Master SQL RoadMap.sh
November 2024 Oracle Certified Foundations Associate Oracle
October 2024 People Leadership Credential Connect
September 2024 12/2024 - Present
Custom Software Engineering Analyst Accenture
Bari, Puglia, Italy · Hybrid
Analysis and development of computer systems through the use of Java and Quarkus in Health and Public Sector. Continuous training on modern technologies for creating customized and efficient software solutions and on agents.
06/2022 - 12/2024
Software analyst and Back End Developer Associate Consultant Links Management and Technology SpA
Experience analyzing as-is software systems and ETL flows using PowerCenter. Completed Spring Boot training for developing modern and scalable backend applications. Backend developer specialized in Spring Boot, with experience in database design, analysis, development and testing of assigned tasks.
02/2021 - 10/2021
Software programmer Adesso.it (prima era WebScience srl)
Experience in AS-IS and TO-BE analysis, SEO evolutions and website evolutions to improve user performance and engagement.
2018 - 2025
Degree in Computer Science University of Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
2013 - 2018
Diploma - Corporate Information Systems Technical Commercial Institute of Maglie
Technical diploma specializing in Business Information Systems, combining IT knowledge with business management.
AI
Observer and Strategy Pattern
Two fundamental behavioral patterns for modern applications. Observer
implements event-driven programming with automatic notifications to subscribers, while
Strategy allows swapping algorithms at runtime without modifying the client.
🎯 What You Will Learn
Observer pattern: pub/sub and event-driven architecture
Implementing reactive systems with Observer
Strategy pattern: interchangeable algorithms
Eliminating if/else with Strategy
Observer Pattern
The Observer pattern (also known as pub/sub) defines a one-to-many
dependency between objects, so that when one object changes state, all of its dependents
are notified automatically.
// Base interfaces
interface Observer {{ '{' }}
update(data: any): void;
{{ '}' }}
interface Subject {{ '{' }}
attach(observer: Observer): void;
detach(observer: Observer): void;
notify(): void;
{{ '}' }}
// Concrete Subject: event source
class NewsAgency implements Subject {{ '{' }}
private observers: Observer[] = [];
private latestNews: string = "";
attach(observer: Observer): void {{ '{' }}
const index = this.observers.indexOf(observer);
if (index === -1) {{ '{' }}
this.observers.push(observer);
console.log(`✅ Observer attached (total: #123;{ '{' }}this.observers.length{{ '}' }})`);
{{ '}' }}
{{ '}' }}
detach(observer: Observer): void {{ '{' }}
const index = this.observers.indexOf(observer);
if (index !== -1) {{ '{' }}
this.observers.splice(index, 1);
console.log(`❌ Observer detached (total: #123;{ '{' }}this.observers.length{{ '}' }})`);
{{ '}' }}
{{ '}' }}
notify(): void {{ '{' }}
console.log(`📢 Notifying #123;{ '{' }}this.observers.length{{ '}' }} observers...`);
for (const observer of this.observers) {{ '{' }}
observer.update(this.latestNews);
{{ '}' }}
{{ '}' }}
publishNews(news: string): void {{ '{' }}
console.log(`📰 Publishing: #123;{ '{' }}news{{ '}' }}`);
this.latestNews = news;
this.notify();
{{ '}' }}
{{ '}' }}
// Concrete Observers
class MobileApp implements Observer {{ '{' }}
constructor(private name: string) {{ '{' }}{{ '}' }}
update(data: any): void {{ '{' }}
console.log(`📱 [#123;{ '{' }}this.name{{ '}' }}] Received: #123;{ '{' }}data{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class EmailSubscriber implements Observer {{ '{' }}
constructor(private email: string) {{ '{' }}{{ '}' }}
update(data: any): void {{ '{' }}
console.log(`📧 [#123;{ '{' }}this.email{{ '}' }}] Sending email: #123;{ '{' }}data{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Usage
const agency = new NewsAgency();
const app1 = new MobileApp("iOS App");
const app2 = new MobileApp("Android App");
const emailer = new EmailSubscriber("user@example.com");
agency.attach(app1);
agency.attach(app2);
agency.attach(emailer);
agency.publishNews("Breaking news: TypeScript 5.0 released!");
// 📰 Publishing: Breaking news: TypeScript 5.0 released!
// 📢 Notifying 3 observers...
// 📱 [iOS App] Received: Breaking news: TypeScript 5.0 released!
// 📱 [Android App] Received: Breaking news: TypeScript 5.0 released!
// 📧 [user@example.com] Sending email: Breaking news: TypeScript 5.0 released!
agency.detach(app2);
agency.publishNews("Update: Bug fixes available");
// ❌ Observer detached (total: 2)
// 📰 Publishing: Update: Bug fixes available
// 📢 Notifying 2 observers...
// 📱 [iOS App] Received: Update: Bug fixes available
// 📧 [user@example.com] Sending email: Update: Bug fixes available
Observer with Push and Pull Models
There are two variants: Push model (Subject sends data) and
Pull model (Observer requests data):
// PUSH MODEL: Subject sends all data
interface PushObserver {{ '{' }}
update(temperature: number, humidity: number, pressure: number): void;
{{ '}' }}
// PULL MODEL: Observer requests only what it needs
interface PullObserver {{ '{' }}
update(subject: WeatherStation): void;
{{ '}' }}
class WeatherStation {{ '{' }}
private observers: PullObserver[] = [];
private temperature: number = 0;
private humidity: number = 0;
private pressure: number = 0;
attach(observer: PullObserver): void {{ '{' }}
this.observers.push(observer);
{{ '}' }}
setMeasurements(temp: number, humidity: number, pressure: number): void {{ '{' }}
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
this.notify();
{{ '}' }}
notify(): void {{ '{' }}
for (const observer of this.observers) {{ '{' }}
observer.update(this); // Passes itself
{{ '}' }}
{{ '}' }}
// Getters for Pull model
getTemperature(): number {{ '{' }} return this.temperature; {{ '}' }}
getHumidity(): number {{ '{' }} return this.humidity; {{ '}' }}
getPressure(): number {{ '{' }} return this.pressure; {{ '}' }}
{{ '}' }}
class TemperatureDisplay implements PullObserver {{ '{' }}
update(subject: WeatherStation): void {{ '{' }}
// Requests only temperature (Pull)
const temp = subject.getTemperature();
console.log(`🌡️ Temperature: #123;{ '{' }}temp{{ '}' }}°C`);
{{ '}' }}
{{ '}' }}
class FullDisplay implements PullObserver {{ '{' }}
update(subject: WeatherStation): void {{ '{' }}
// Requests all data
console.log(`🌦️ Weather: #123;{ '{' }}subject.getTemperature(){{ '}' }}°C, #123;{ '{' }}subject.getHumidity(){{ '}' }}%, #123;{ '{' }}subject.getPressure(){{ '}' }}hPa`);
{{ '}' }}
{{ '}' }}
// Usage
const station = new WeatherStation();
station.attach(new TemperatureDisplay());
station.attach(new FullDisplay());
station.setMeasurements(25, 65, 1013);
// 🌡️ Temperature: 25°C
// 🌦️ Weather: 25°C, 65%, 1013hPa
Observer Pattern in Angular
Angular uses Observer extensively through RxJS Observables :
import {{ '{' }} Subject {{ '}' }} from 'rxjs';
// Subject = Observable + Observer combined
class MessageService {{ '{' }}
private messageSubject = new Subject<string>();
// Observable for subscribers
message$ = this.messageSubject.asObservable();
// Method for publishing
sendMessage(message: string): void {{ '{' }}
this.messageSubject.next(message);
{{ '}' }}
{{ '}' }}
// Usage in Angular components
class ComponentA {{ '{' }}
constructor(private messageService: MessageService) {{ '{' }}{{ '}' }}
sendNotification(): void {{ '{' }}
this.messageService.sendMessage("Hello from Component A");
{{ '}' }}
{{ '}' }}
class ComponentB {{ '{' }}
constructor(private messageService: MessageService) {{ '{' }}
// Subscribe to receive messages
this.messageService.message$.subscribe(msg => {{ '{' }}
console.log(`ComponentB received: #123;{ '{' }}msg{{ '}' }}`);
{{ '}' }});
{{ '}' }}
{{ '}' }}
class ComponentC {{ '{' }}
constructor(private messageService: MessageService) {{ '{' }}
this.messageService.message$.subscribe(msg => {{ '{' }}
console.log(`ComponentC received: #123;{ '{' }}msg{{ '}' }}`);
{{ '}' }});
{{ '}' }}
{{ '}' }}
Strategy Pattern
The Strategy pattern defines a family of algorithms, encapsulates
each one in a separate class, and makes them interchangeable. Strategy allows
algorithms to vary independently from the clients that use them.
// Strategy Interface
interface PaymentStrategy {{ '{' }}
pay(amount: number): void;
{{ '}' }}
// Concrete Strategies
class CreditCardPayment implements PaymentStrategy {{ '{' }}
constructor(
private cardNumber: string,
private cvv: string
) {{ '{' }}{{ '}' }}
pay(amount: number): void {{ '{' }}
console.log(`💳 Paid #123;{ '{' }}amount{{ '}' }} with Credit Card ending in #123;{ '{' }}this.cardNumber.slice(-4){{ '}' }}`);
{{ '}' }}
{{ '}' }}
class PayPalPayment implements PaymentStrategy {{ '{' }}
constructor(private email: string) {{ '{' }}{{ '}' }}
pay(amount: number): void {{ '{' }}
console.log(`🅿️ Paid #123;{ '{' }}amount{{ '}' }} via PayPal (#123;{ '{' }}this.email{{ '}' }})`);
{{ '}' }}
{{ '}' }}
class CryptoPayment implements PaymentStrategy {{ '{' }}
constructor(private walletAddress: string) {{ '{' }}{{ '}' }}
pay(amount: number): void {{ '{' }}
console.log(`₿ Paid #123;{ '{' }}amount{{ '}' }} in crypto to #123;{ '{' }}this.walletAddress.slice(0, 8){{ '}' }}...`);
{{ '}' }}
{{ '}' }}
// Context: uses the strategy
class ShoppingCart {{ '{' }}
private items: {{ '{' }} name: string; price: number {{ '}' }}[] = [];
private paymentStrategy?: PaymentStrategy;
addItem(name: string, price: number): void {{ '{' }}
this.items.push({{ '{' }} name, price {{ '}' }});
{{ '}' }}
setPaymentStrategy(strategy: PaymentStrategy): void {{ '{' }}
this.paymentStrategy = strategy;
{{ '}' }}
checkout(): void {{ '{' }}
const total = this.items.reduce((sum, item) => sum + item.price, 0);
console.log(`🛒 Total: ${{ '{' }}total{{ '}' }}`);
if (!this.paymentStrategy) {{ '{' }}
console.log("❌ No payment method selected");
return;
{{ '}' }}
this.paymentStrategy.pay(total);
this.items = []; // Clear cart
{{ '}' }}
{{ '}' }}
// Usage
const cart = new ShoppingCart();
cart.addItem("Laptop", 1200);
cart.addItem("Mouse", 25);
// Use different payment strategies
cart.setPaymentStrategy(new CreditCardPayment("1234567890123456", "123"));
cart.checkout();
// 🛒 Total: $1225
// 💳 Paid 1225 with Credit Card ending in 3456
cart.addItem("Keyboard", 75);
cart.setPaymentStrategy(new PayPalPayment("user@paypal.com"));
cart.checkout();
// 🛒 Total: $75
// 🅿️ Paid 75 via PayPal (user@paypal.com)
cart.addItem("Monitor", 350);
cart.setPaymentStrategy(new CryptoPayment("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8"));
cart.checkout();
// 🛒 Total: $350
// ₿ Paid 350 in crypto to 0x742d35...
Strategy for Eliminating If/Else
One of the main uses of Strategy is eliminating long if/else chains:
// ❌ Procedural approach with if/else
class ShippingCalculator {{ '{' }}
calculateCost(weight: number, type: string): number {{ '{' }}
if (type === 'standard') {{ '{' }}
return weight * 1.5;
{{ '}' }} else if (type === 'express') {{ '{' }}
return weight * 3.0;
{{ '}' }} else if (type === 'overnight') {{ '{' }}
return weight * 5.0;
{{ '}' }} else if (type === 'international') {{ '{' }}
return weight * 7.5;
{{ '}' }} else {{ '{' }}
throw new Error("Unknown shipping type");
{{ '}' }}
{{ '}' }}
{{ '}' }}
interface ShippingStrategy {{ '{' }}
calculate(weight: number): number;
{{ '}' }}
class StandardShipping implements ShippingStrategy {{ '{' }}
calculate(weight: number): number {{ '{' }}
return weight * 1.5;
{{ '}' }}
{{ '}' }}
class ExpressShipping implements ShippingStrategy {{ '{' }}
calculate(weight: number): number {{ '{' }}
return weight * 3.0;
{{ '}' }}
{{ '}' }}
class OvernightShipping implements ShippingStrategy {{ '{' }}
calculate(weight: number): number {{ '{' }}
return weight * 5.0;
{{ '}' }}
{{ '}' }}
class InternationalShipping implements ShippingStrategy {{ '{' }}
calculate(weight: number): number {{ '{' }}
return weight * 7.5;
{{ '}' }}
{{ '}' }}
// Context with Strategy Map
class ShippingCalculatorV2 {{ '{' }}
private strategies: Map<string, ShippingStrategy> = new Map([
['standard', new StandardShipping()],
['express', new ExpressShipping()],
['overnight', new OvernightShipping()],
['international', new InternationalShipping()],
]);
calculateCost(weight: number, type: string): number {{ '{' }}
const strategy = this.strategies.get(type);
if (!strategy) {{ '{' }}
throw new Error(`Unknown shipping type: #123;{ '{' }}type{{ '}' }}`);
{{ '}' }}
return strategy.calculate(weight);
{{ '}' }}
// Easy to add new strategies
addStrategy(type: string, strategy: ShippingStrategy): void {{ '{' }}
this.strategies.set(type, strategy);
{{ '}' }}
{{ '}' }}
// Usage
const calculator = new ShippingCalculatorV2();
console.log(calculator.calculateCost(10, 'standard')); // 15
console.log(calculator.calculateCost(10, 'overnight')); // 50
// Add custom strategy
class SameDayShipping implements ShippingStrategy {{ '{' }}
calculate(weight: number): number {{ '{' }} return weight * 8.0; {{ '}' }}
{{ '}' }}
calculator.addStrategy('sameday', new SameDayShipping());
console.log(calculator.calculateCost(10, 'sameday')); // 80
Strategy with Dynamic Validation
Practical example: validation system with interchangeable rules:
interface ValidationStrategy {{ '{' }}
validate(value: string): boolean;
getErrorMessage(): string;
{{ '}' }}
class EmailValidator implements ValidationStrategy {{ '{' }}
validate(value: string): boolean {{ '{' }}
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
{{ '}' }}
getErrorMessage(): string {{ '{' }}
return "Invalid email format";
{{ '}' }}
{{ '}' }}
class PasswordValidator implements ValidationStrategy {{ '{' }}
validate(value: string): boolean {{ '{' }}
// Min 8 characters, at least one uppercase, one number
return value.length >= 8 && /[A-Z]/.test(value) && /[0-9]/.test(value);
{{ '}' }}
getErrorMessage(): string {{ '{' }}
return "Password must be 8+ chars with uppercase and number";
{{ '}' }}
{{ '}' }}
class PhoneValidator implements ValidationStrategy {{ '{' }}
validate(value: string): boolean {{ '{' }}
return /^\+?[1-9]\d{{ '{' }}9,14{{ '}' }}$/.test(value.replace(/\s/g, ''));
{{ '}' }}
getErrorMessage(): string {{ '{' }}
return "Invalid phone number format";
{{ '}' }}
{{ '}' }}
class FormField {{ '{' }}
private validator?: ValidationStrategy;
constructor(
private name: string,
private value: string
) {{ '{' }}{{ '}' }}
setValidator(validator: ValidationStrategy): void {{ '{' }}
this.validator = validator;
{{ '}' }}
validate(): {{ '{' }} valid: boolean; error?: string {{ '}' }} {{ '{' }}
if (!this.validator) {{ '{' }}
return {{ '{' }} valid: true {{ '}' }};
{{ '}' }}
const valid = this.validator.validate(this.value);
return {{ '{' }}
valid,
error: valid ? undefined : this.validator.getErrorMessage()
{{ '}' }};
{{ '}' }}
{{ '}' }}
// Usage
const emailField = new FormField("email", "user@example.com");
emailField.setValidator(new EmailValidator());
console.log(emailField.validate()); // {{ '{' }} valid: true {{ '}' }}
const passwordField = new FormField("password", "weak");
passwordField.setValidator(new PasswordValidator());
console.log(passwordField.validate());
// {{ '{' }} valid: false, error: 'Password must be 8+ chars with uppercase and number' {{ '}' }}
const phoneField = new FormField("phone", "+1234567890");
phoneField.setValidator(new PhoneValidator());
console.log(phoneField.validate()); // {{ '{' }} valid: true {{ '}' }}
When to Use Each Pattern
✅ Use Observer when:
You need to automatically notify multiple objects about state changes
You want to decouple publisher from subscriber
Event-driven architecture, real-time updates
Notification systems, live dashboards, chat
✅ Use Strategy when:
You have a family of interchangeable algorithms
You want to avoid long if/else chains
The algorithm must change at runtime
Sorting, validation, payment processing, compression
Practical Example: E-Commerce with Both
// STRATEGY: Pricing strategies
interface PricingStrategy {{ '{' }}
calculatePrice(basePrice: number): number;
{{ '}' }}
class RegularPricing implements PricingStrategy {{ '{' }}
calculatePrice(basePrice: number): number {{ '{' }}
return basePrice;
{{ '}' }}
{{ '}' }}
class BlackFridayPricing implements PricingStrategy {{ '{' }}
calculatePrice(basePrice: number): number {{ '{' }}
return basePrice * 0.5; // 50% off
{{ '}' }}
{{ '}' }}
class MemberPricing implements PricingStrategy {{ '{' }}
calculatePrice(basePrice: number): number {{ '{' }}
return basePrice * 0.9; // 10% off
{{ '}' }}
{{ '}' }}
// OBSERVER: Price observers
interface PriceObserver {{ '{' }}
onPriceChange(productName: string, oldPrice: number, newPrice: number): void;
{{ '}' }}
class PriceAlertService implements PriceObserver {{ '{' }}
onPriceChange(productName: string, oldPrice: number, newPrice: number): void {{ '{' }}
console.log(`🔔 Alert: #123;{ '{' }}productName{{ '}' }} price changed from ${{ '{' }}oldPrice{{ '}' }} to ${{ '{' }}newPrice{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class AnalyticsService implements PriceObserver {{ '{' }}
onPriceChange(productName: string, oldPrice: number, newPrice: number): void {{ '{' }}
console.log(`📊 Analytics: Price change recorded for #123;{ '{' }}productName{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Product with Strategy + Observer
class Product {{ '{' }}
private observers: PriceObserver[] = [];
private pricingStrategy: PricingStrategy = new RegularPricing();
private currentPrice: number;
constructor(
private name: string,
private basePrice: number
) {{ '{' }}
this.currentPrice = this.calculatePrice();
{{ '}' }}
attach(observer: PriceObserver): void {{ '{' }}
this.observers.push(observer);
{{ '}' }}
setPricingStrategy(strategy: PricingStrategy): void {{ '{' }}
this.pricingStrategy = strategy;
this.updatePrice();
{{ '}' }}
private calculatePrice(): number {{ '{' }}
return this.pricingStrategy.calculatePrice(this.basePrice);
{{ '}' }}
private updatePrice(): void {{ '{' }}
const oldPrice = this.currentPrice;
this.currentPrice = this.calculatePrice();
if (oldPrice !== this.currentPrice) {{ '{' }}
this.notifyObservers(oldPrice, this.currentPrice);
{{ '}' }}
{{ '}' }}
private notifyObservers(oldPrice: number, newPrice: number): void {{ '{' }}
for (const observer of this.observers) {{ '{' }}
observer.onPriceChange(this.name, oldPrice, newPrice);
{{ '}' }}
{{ '}' }}
getPrice(): number {{ '{' }}
return this.currentPrice;
{{ '}' }}
{{ '}' }}
// Combined usage
const laptop = new Product("Gaming Laptop", 1500);
laptop.attach(new PriceAlertService());
laptop.attach(new AnalyticsService());
console.log(`Initial price: ${{ '{' }}laptop.getPrice(){{ '}' }}`); // 1500
laptop.setPricingStrategy(new BlackFridayPricing());
// 🔔 Alert: Gaming Laptop price changed from $1500 to $750
// 📊 Analytics: Price change recorded for Gaming Laptop
console.log(`Black Friday price: ${{ '{' }}laptop.getPrice(){{ '}' }}`); // 750
laptop.setPricingStrategy(new MemberPricing());
// 🔔 Alert: Gaming Laptop price changed from $750 to $1350
// 📊 Analytics: Price change recorded for Gaming Laptop
console.log(`Member price: ${{ '{' }}laptop.getPrice(){{ '}' }}`); // 1350
Conclusion
Observer and Strategy are fundamental patterns in modern applications. Observer
is at the heart of event-driven programming (RxJS, DOM events, notifications), while
Strategy eliminates procedural code by replacing if/else with polymorphism.
Combining them creates reactive and flexible systems.
🎯 Key Takeaways
Observer = pub/sub, automatic notifications, event-driven
Push model (Subject sends data) vs Pull model (Observer requests)
Strategy = interchangeable algorithms, eliminates if/else
Use Observer for reactive systems and notifications
Use Strategy for variable runtime behaviors
RxJS in Angular = enhanced Observer pattern