今日已更新 35 条资讯 | 累计 37284 条内容
关于我们

标签:#designpatterns

找到 10 篇相关文章

AI 资讯

The State Pattern Trap: Why GoF Is Not Always the Best Choice

Have you ever tried to use the classic Gang of Four (GoF) State Pattern in real code? You might have hit a wall. You might have thought, "Wait, this feels way too connected." You are not wrong about that. In school and many engineering interviews, the GoF State Pattern looks great. It promises to fix big, ugly switch statements. But real business rules are hard. When you use this pattern in real life, it can become a huge mess. Every state knows too much about the other states. Let us look at why this happens. We will learn the difference between the GoF pattern and a Finite State Machine (FSM). We will also learn when to use each one. The False Promise of the GoF State Pattern The main idea of the GoF State Pattern is to spread out the work. The main object gives its work to state objects. But there is a catch. The state classes themselves must trigger the change to the next state. Example: The Traffic Light Think about a simple traffic light. It goes Red to Green to Yellow to Red. It does this forever. class RedState implements TrafficLightState { change ( context : TrafficLight ): void { console . log ( " RED light, Stop " ); context . setState ( new GreenState ()); // Very connected! } } The Problem: RedState is forced to know about GreenState . This is fine for a simple traffic light. It is a closed loop. The rules will never change. But what happens when business rules change? Imagine the city council makes a new rule. From midnight to 5:00 AM, the light must flash yellow. Now, you must open your RedState and YellowState classes. You have to add new time checks. You have to add the new flashing state. The more states you add, the messier your code gets. The Better Choice: The Central FSM In the real world, things do not always happen in a straight line. An online order does not just go from Pending to Shipped to Delivered. It can jump from Pending to Cancelled. It can go from Shipped to Returned. If you use GoF here, your PendingState needs to know about many

2026-08-26 原文 →
AI 资讯

The Criteria pattern in NestJS: what a client may ask for is a file, not a signature

The Criteria pattern in NestJS One single way to filter, sort and paginate any list. Five parameters and a find() The example running through this article is a library catalogue. A book stores this: // src/book/book.schema.ts @ Schema ({ timestamps : true }) export class Book { @ Prop () title : string ; @ Prop ({ type : Types . ObjectId , ref : " Author " }) author : Types . ObjectId ; @ Prop () publishedAt : Date ; @ Prop () copies : number ; // copies on the shelf @ Prop () available : boolean ; @ Prop () acquisitionPrice : number ; // what it cost us: internal, never published } The author's name is not here: it lives in the authors collection, on the other side of that reference. And the screen consuming the catalogue is a table with a search box, per-column filters and pagination. The endpoint feeding it is written once and grows by accretion. It starts returning a page with a fixed order, and by the time the table has all its filters it has become this: // src/book/book.controller.ts @ Controller ( " books " ) export class BookController { constructor ( @ InjectModel ( Book . name ) private readonly model : Model < BookDocument > , ) {} @ Get () async getAll ( @ Query ( " title " ) title ?: string , @ Query ( " available " ) available ?: string , @ Query ( " minCopies " ) minCopies ?: string , @ Query ( " sortBy " ) sortBy ?: string , @ Query ( " page " ) page ?: string , ) { const filter : FilterQuery < BookDocument > = {}; if ( title ) { filter . title = { $regex : title , $options : " i " }; } if ( available ) { filter . available = available === " true " ; } if ( minCopies ) { filter . copies = { $gte : Number ( minCopies ) }; } const current = Number ( page ?? 1 ); const [ items , total ] = await Promise . all ([ this . model . find ( filter ) . sort ({ [ sortBy ?? " createdAt " ]: - 1 }) . skip (( current - 1 ) * 20 ) . limit ( 20 ), this . model . countDocuments ( filter ), ]); return { items : items , total : total , page : current }; } } There are co

2026-08-21 原文 →
AI 资讯

El patrón Criteria en NestJS: lo que un cliente puede pedir es un archivo, no una firma

Cinco parámetros y un find() El ejemplo de todo el artículo es el catálogo de una biblioteca. Un libro guarda esto: // src/book/book.schema.ts @ Schema ({ timestamps : true }) export class Book { @ Prop () title : string ; @ Prop ({ type : Types . ObjectId , ref : " Author " }) author : Types . ObjectId ; @ Prop () publishedAt : Date ; @ Prop () copies : number ; // ejemplares en la estantería @ Prop () available : boolean ; @ Prop () acquisitionPrice : number ; // lo que costó adquirirlo: interno, no se publica } El nombre del autor no está aquí: vive en la colección authors , al otro lado de esa referencia. Y la pantalla que consume el catálogo es una tabla con buscador, filtros por columna y paginación. El endpoint que la alimenta se escribe una vez y crece por acumulación. Empieza devolviendo una página con un orden fijo, y para cuando la tabla tiene todos sus filtros ha llegado a esto: // src/book/book.controller.ts @ Controller ( " books " ) export class BookController { constructor ( @ InjectModel ( Book . name ) private readonly model : Model < BookDocument > , ) {} @ Get () async getAll ( @ Query ( " title " ) title ?: string , @ Query ( " available " ) available ?: string , @ Query ( " minCopies " ) minCopies ?: string , @ Query ( " sortBy " ) sortBy ?: string , @ Query ( " page " ) page ?: string , ) { const filter : FilterQuery < BookDocument > = {}; if ( title ) { filter . title = { $regex : title , $options : " i " }; } if ( available ) { filter . available = available === " true " ; } if ( minCopies ) { filter . copies = { $gte : Number ( minCopies ) }; } const current = Number ( page ?? 1 ); const [ items , total ] = await Promise . all ([ this . model . find ( filter ) . sort ({ [ sortBy ?? " createdAt " ]: - 1 }) . skip (( current - 1 ) * 20 ) . limit ( 20 ), this . model . countDocuments ( filter ), ]); return { items : items , total : total , page : current }; } } El método tiene decisiones correctas dentro: el total sale del mismo filtro que los

2026-08-21 原文 →
开发者

LISKOV SUBSTITUTION PRINCIPLE

A parent class must be able to be substituted by its child classes without breaking the application. In practice, this helps to organize the idea of inheritance, as it prevents us from extending a parent class only to later remove an already implemented method or do a “throw new Error(‘Not implemented’)”. Making us much more careful during planning. THE BIGGEST SYMPTOM OF ERROR Unfortunately, it is a symptom that appears late, but it is exactly when we are going to make a new implementation. You realize you violated Liskov when you are going to build a class or subclass and need to purposely throw an error in the implementation of a method. Exactly because that method shouldn't be there, but it is. A BAD EXAMPLE For example, in a delivery system. In this case, the “Delivery” class should be the parent/base for the other implementations. But the ‘MotoboyDelivery’ class breaks this. Code Example: // BAD: The subclass breaks the parent class contract. class Delivery { public calculateShipping (): number { return 15.0 ; } public getTrackingCode (): string { return " TRK123456789 " ; } } class MotoboyDelivery extends Delivery { public calculateShipping (): number { return 8.0 ; } // ERROR! There is no tracking code. public getTrackingCode (): string { throw new Error ( " Motoboys do not have a tracking code. " ); } } THE SOLUTION For those who do not yet know the 'Liskov Substitution Principle', it might seem that fitting in a sequence of 'if's is the solution. But in reality, the ideal path is to rethink how this abstraction is built. A good guiding principle is to think that a child class must always be able to take the place of the parent, without breaking the application. A GOOD EXAMPLE Still in the delivery system. ‘Delivery’ now has ‘TrackableDelivery’ in the middle of the way. With this, each “leaf”/edge of the application inherits what makes the most sense and nothing is broken. Code Example: interface Delivery { calculateShipping (): number ; } interface Trackab

2026-08-20 原文 →
AI 资讯

Design Patterns: Reusable Solutions to Recurring Problems

Design Patterns: Reusable Solutions to Recurring Problems A practical guide to classic design patterns in C#/.NET — Factory, Singleton, Repository, Strategy, and Mediator — covering what problem each one actually solves, working implementations, common .NET-specific variations, and honest guidance on when each pattern earns its complexity versus when it's unnecessary ceremony. Table of Contents Introduction Factory Pattern Singleton Pattern Repository Pattern Strategy Pattern Mediator Pattern How These Patterns Combine in Practice Patterns vs. Over-Engineering Common Pitfalls Quick Reference Table Conclusion Introduction Design patterns are named, reusable solutions to problems that recur often enough across software projects that giving them a shared name and shape is genuinely useful — not because the specific code is copy-pasteable, but because the name lets developers communicate a design intent quickly ("just make it a Strategy") instead of re-explaining the same structural idea from scratch every time. This guide covers five of the most commonly used patterns in .NET codebases, with working C# examples, and — consistent with this series' recurring theme — honest guidance on when each pattern is solving a genuine problem versus adding structure a simpler solution wouldn't need. // A pattern name compresses a whole design conversation into one word "Just inject an IPaymentStrategy and pick the implementation based on the payment method" // ← Strategy "Wrap the whole multi-step checkout process behind a single mediator call" // ← Mediator 1. Factory Pattern The problem: object creation logic that doesn't belong at the call site // ❌ The caller needs to know about every concrete shipping provider and how to construct each one IShippingProvider provider = order . Region switch { "US" => new UpsShippingProvider ( apiKey , region ), "EU" => new DhlShippingProvider ( apiKey , endpoint ), "APAC" => new FedExShippingProvider ( apiKey , credentials ), _ => throw new NotS

2026-08-18 原文 →
AI 资讯

SOLID Design Principles: Stop Writing Code That Breaks When You Touch It

Guidelines, not rules. Here's the difference — and why it matters. What is SOLID? SOLID is a set of software design guidelines — not hard rules, but principles that guide how we organize our code. The goal is simple: as your codebase grows and your team scales, things should get easier to change, not harder. SOLID is what makes that possible. Five principles. One goal. Let's walk through each one with real code. S — Single Responsibility Principle A class, function, or method should have one and only one reason to change. The Violation class Bird : def __init__ ( self , name : str , bird_type : str ): self . name = name self . bird_type = bird_type def make_sound ( self ): # two jobs — deciding the type AND making the sound if self . bird_type == " parrot " : print ( " Squawk! " ) elif self . bird_type == " eagle " : print ( " Screech! " ) elif self . bird_type == " owl " : print ( " Hoot! " ) else : print ( " ... " ) make_sound() has two responsibilities — deciding which bird type it is AND making the sound. That's two reasons to change. Add a new bird? Touch make_sound() . Change how sounds work? Touch make_sound() again. Two different reasons, one method. SRP violated. The Fix from abc import ABC , abstractmethod class Bird ( ABC ): def __init__ ( self , name : str ): self . name = name @abstractmethod def make_sound ( self ): pass class Parrot ( Bird ): def make_sound ( self ): print ( " Squawk! " ) class Eagle ( Bird ): def make_sound ( self ): print ( " Screech! " ) class Owl ( Bird ): def make_sound ( self ): print ( " Hoot! " ) # Usage birds = [ Parrot ( " Polly " ), Eagle ( " Sam " ), Owl ( " Oliver " )] for bird in birds : bird . make_sound () Now each class has one responsibility. Parrot.make_sound() only changes if parrots change how they sound. Nothing else touches it. O — Open/Closed Principle A class should be open for extension but closed for modification. SRP and OCP go hand in hand. When you fixed SRP in the Bird example above — you also fixed OCP.

2026-07-22 原文 →
AI 资讯

Your Error Messages Are Written for Developers, Not Users

Open the network tab on almost any web app, trigger a failed request, and you'll usually find one of two things staring back at the user: a raw stack trace, or a message so generic it might as well say "something happened." Neither one helps. Both exist for the same reason they were written by developers, for developers, and never translated for the person actually using the product. The Error Message Nobody Designed Most UI elements go through some level of design scrutiny. Buttons get spacing decisions. Forms get validation states. But error messages? They're usually whatever string got thrown at the moment something broke, copy-pasted straight from a try/catch block into a toast notification. "Error 500: Internal Server Error." "Failed to fetch." "Unexpected token in JSON at position 4." These are diagnostic breadcrumbs for engineers debugging a system. To a user trying to submit a form or complete a purchase, they're just noise confirmation that something went wrong, with zero indication of what to do next. This is where good web app design services earn their keep not in the buttons and layouts everyone notices, but in the failure states nobody plans for until users start complaining. Why This Keeps Happening It's not that teams don't care. It's that error handling sits at the intersection of two disciplines that rarely talk to each other at the moment. Backend logic throws whatever exception the code produces. The front end just needs something to display so the app doesn't silently freeze. Nobody's job, at that moment, is to ask: "what should the user actually understand right now?" The result is a UI layer that's polished everywhere except the one place users encounter when things go wrong which, ironically, is exactly when clear communication matters most. What a Good Error Message Actually Does A well-designed error message does three things a raw exception never does. It tells the user what happened, in plain language not "Error: NetworkException," but "W

2026-07-21 原文 →
开发者

Making ServiceLoader usable: a provider factory

I keep coming back to java.util.ServiceLoader . I have used it to put a JSON layer behind a contract, so the core code carries no direct dependency on any particular JSON library and I can swap the implementation without touching callers. The same shape works for JWT handling, where the concrete library might be jose4j or another JOSE implementation, and you can easily find other decoupling use-cases. The motivation is always the same: the application should depend on a capability, not on a vendor. A while back I wrote about exactly that idea in Rediscovering Java ServiceLoader: Beyond Plugins and Into Capabilities , where the argument was to treat ServiceLoader as capability discovery rather than a plugin system. That piece hit the limitation everyone hits — the no-argument constructor — and worked around it with a default constructor plus a dynamic proxy that built the real object through a factory on each call. It works, but it is indirection bolted on after the fact, not a design. This post is the part I never pinned down back then: turning that workaround into a small, explicit pattern. The running example below is a mock payments system, with Stripe and PayPal specializations, because it is compact enough to show end to end. The JSON and JWT cases cited can be built with the same structure. The two limits ServiceLoader leaves you The first is the no-argument constructor. Whatever ServiceLoader instantiates must have a public, parameterless constructor. My StripePaymentService takes an API key, so it cannot be the class ServiceLoader loads — not unless I bolt on some init-after-construction step, which I would rather avoid. The second is selection, or rather the lack of it. ServiceLoader gives you every implementation it finds, in roughly classpath order. There is no id, nothing to prioritise on, and no way to ask whether a given one even applies in the current environment. With two backends on the classpath and only one configured, working out which to use is

2026-07-14 原文 →
AI 资讯

Enterprise Design Patterns in Python: Repository & Unit of Work — Real-World E-Commerce Example

Enterprise Design Patterns in Python: Repository & Unit of Work 🐍🏗️ Series: Enterprise Application Architecture | Source: Fowler's EAA Catalog | Code: GitHub Repository 🧠 What Are Enterprise Design Patterns? Martin Fowler's Patterns of Enterprise Application Architecture (2002) is one of the most influential books in software engineering. It documents recurring architectural solutions — patterns — that solve common problems in enterprise systems: how to organize domain logic, how to talk to databases, how to handle transactions, and more. In this article, we'll explore two of the most powerful and widely-used patterns from that catalog: Pattern Category Core Purpose Repository Data Source Abstracts data access behind a collection-like interface Unit of Work Data Source Tracks object changes and commits them as a single transaction These two patterns work beautifully together — and you'll see exactly why with a real-world example. 🛒 The Problem: An E-Commerce Order System Imagine you're building a backend for an online store. When a customer places an order: A new Order is created Each Product 's stock is decremented A Payment record is registered If any of these steps fail midway, the entire operation should roll back — no partial state. This is exactly the problem the Unit of Work pattern solves, and the Repository pattern makes it all cleanly testable. 📁 Repository Pattern Definition "A Repository mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects." — Martin Fowler, PoEAA The Repository acts as an in-memory collection of domain objects. Your business logic never knows if it's talking to PostgreSQL, SQLite, or even a mock list — it just calls .add() , .get() , .list() . Domain Model # models.py from dataclasses import dataclass , field from typing import List from uuid import uuid4 @dataclass class Product : id : str name : str price : float stock : int @dataclass class OrderItem : product_id : str qua

2026-06-20 原文 →