<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Design-Patterns on</title><link>/tags/design-patterns/</link><description>Recent content in Design-Patterns on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 22 Oct 2025 12:00:00 +0000</lastBuildDate><atom:link href="/tags/design-patterns/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 10: Interpreter Pattern — DSLs and parsing</title><link>/post/rust/rust-dp-interpreter/</link><pubDate>Wed, 22 Oct 2025 12:00:00 +0000</pubDate><guid>/post/rust/rust-dp-interpreter/</guid><description>&lt;p&gt;A few years ago I needed to let non-technical users define filtering rules for a data pipeline. The options were: embed Lua, use a YAML config with increasingly awkward syntax, or write a small domain-specific language. I picked the DSL. It took two days in Rust, and the result was a type-safe, sandboxed expression evaluator that couldn&amp;rsquo;t crash the host program no matter what users typed. Try getting that guarantee with dynamic code execution in Python.&lt;/p&gt;</description></item><item><title>Lesson 9: Entity Component System — Data-oriented design</title><link>/post/rust/rust-dp-ecs/</link><pubDate>Sun, 19 Oct 2025 15:30:00 +0000</pubDate><guid>/post/rust/rust-dp-ecs/</guid><description>&lt;p&gt;I spent years thinking about game objects the OOP way. A &lt;code&gt;Player&lt;/code&gt; extends &lt;code&gt;Character&lt;/code&gt; extends &lt;code&gt;Entity&lt;/code&gt;. A &lt;code&gt;Goblin&lt;/code&gt; extends &lt;code&gt;Enemy&lt;/code&gt; extends &lt;code&gt;Character&lt;/code&gt; extends &lt;code&gt;Entity&lt;/code&gt;. Then someone asks &amp;ldquo;what if a goblin can be mind-controlled and act like a player?&amp;rdquo; and your inheritance hierarchy collapses.&lt;/p&gt;
&lt;p&gt;ECS — Entity Component System — is the answer that the game development world converged on, and it&amp;rsquo;s fundamentally a Rust-shaped idea. Data and behavior are separated. Composition replaces inheritance. Cache-friendly memory layouts replace pointer-chasing object graphs. Rust&amp;rsquo;s ownership model maps onto ECS so naturally that Bevy — the most popular Rust game engine — is built entirely around it.&lt;/p&gt;</description></item><item><title>Lesson 8: Middleware / Chain of Responsibility — Tower-style</title><link>/post/rust/rust-dp-middleware/</link><pubDate>Thu, 16 Oct 2025 07:15:00 +0000</pubDate><guid>/post/rust/rust-dp-middleware/</guid><description>&lt;p&gt;If you&amp;rsquo;ve built anything with Express, Koa, or ASP.NET, you know middleware. A request comes in, passes through a chain of handlers — logging, auth, rate limiting, CORS — and eventually reaches your application logic. Each handler can modify the request, short-circuit the chain, or pass it along.&lt;/p&gt;
&lt;p&gt;The Chain of Responsibility pattern from the GoF book is basically the same thing. The difference is branding.&lt;/p&gt;
&lt;p&gt;What makes this pattern interesting in Rust is &lt;code&gt;tower&lt;/code&gt; — the crate that defines how middleware works across the entire Rust async ecosystem. Axum, Tonic, Hyper — they all use Tower&amp;rsquo;s &lt;code&gt;Service&lt;/code&gt; trait. Understanding it unlocks the middleware patterns in all of these frameworks.&lt;/p&gt;</description></item><item><title>Lesson 7: Repository Pattern — Abstracting storage</title><link>/post/rust/rust-dp-repository/</link><pubDate>Mon, 13 Oct 2025 13:45:00 +0000</pubDate><guid>/post/rust/rust-dp-repository/</guid><description>&lt;p&gt;Every backend developer eventually writes the same code: a function that takes a database connection, runs a query, maps the rows to a struct, and returns it. Then you write another one. And another. Pretty soon your business logic is tangled up with SQL strings and connection pool handles, and testing anything requires a running database.&lt;/p&gt;
&lt;p&gt;The Repository pattern fixes this. It&amp;rsquo;s old — Martin Fowler wrote about it in 2002 — but the way Rust implements it is genuinely different from what you&amp;rsquo;d do in Java or C#. Rust&amp;rsquo;s trait system, combined with generics and lifetimes, gives you a repository abstraction that&amp;rsquo;s zero-cost in production and trivially mockable in tests.&lt;/p&gt;</description></item><item><title>Lesson 6: Factory Patterns — When constructors aren't enough</title><link>/post/rust/rust-dp-factory/</link><pubDate>Sat, 11 Oct 2025 10:00:00 +0000</pubDate><guid>/post/rust/rust-dp-factory/</guid><description>&lt;p&gt;Here&amp;rsquo;s a hot take: most &amp;ldquo;Factory pattern&amp;rdquo; usage in Java and C# exists purely to work around limitations of constructors. Constructors can&amp;rsquo;t have descriptive names. They can&amp;rsquo;t return a different subtype. They can&amp;rsquo;t fail gracefully. So you wrap them in a static method and call it a Factory.&lt;/p&gt;
&lt;p&gt;Rust doesn&amp;rsquo;t have constructors at all. Every struct is constructed directly, and associated functions already do what Factory Method does in OOP. So do you even need the Factory pattern in Rust?&lt;/p&gt;</description></item><item><title>Lesson 5: Decorator Pattern — Wrapping with trait composition</title><link>/post/rust/rust-dp-decorator/</link><pubDate>Thu, 09 Oct 2025 16:30:00 +0000</pubDate><guid>/post/rust/rust-dp-decorator/</guid><description>&lt;p&gt;I remember the moment Decorator clicked for me. I was reading the source for Java&amp;rsquo;s I/O library — &lt;code&gt;BufferedInputStream&lt;/code&gt; wrapping &lt;code&gt;FileInputStream&lt;/code&gt; wrapping &lt;code&gt;InputStream&lt;/code&gt;. Three layers deep, each adding behavior without subclassing. Elegant. Then I tried to write the same thing in Rust and learned that &amp;ldquo;wrapping a thing while preserving its interface&amp;rdquo; is a fundamentally different exercise when you don&amp;rsquo;t have inheritance.&lt;/p&gt;
&lt;p&gt;The good news? Rust&amp;rsquo;s version is often &lt;em&gt;better&lt;/em&gt; than the OOP original, because composition is the default and the compiler enforces the contracts.&lt;/p&gt;</description></item><item><title>Lesson 4: Command Pattern — Closures as commands</title><link>/post/rust/rust-dp-command/</link><pubDate>Tue, 07 Oct 2025 09:20:00 +0000</pubDate><guid>/post/rust/rust-dp-command/</guid><description>&lt;p&gt;The Command pattern has always struck me as one of those patterns that&amp;rsquo;s really just &amp;ldquo;wrap a function call in an object.&amp;rdquo; In Java, you create a &lt;code&gt;Command&lt;/code&gt; interface with an &lt;code&gt;execute()&lt;/code&gt; method, make a class for each command, and pass them around. It takes about 40 lines of boilerplate to do what a lambda does in one.&lt;/p&gt;
&lt;p&gt;In Rust, closures &lt;em&gt;are&lt;/em&gt; the Command pattern. But when you need undo, history, or serialization, you still need the structured version — and Rust&amp;rsquo;s ownership model makes the undo/redo part surprisingly elegant.&lt;/p&gt;</description></item><item><title>Lesson 3: Observer Pattern — Channels and callbacks in Rust</title><link>/post/rust/rust-dp-observer/</link><pubDate>Sun, 05 Oct 2025 11:45:00 +0000</pubDate><guid>/post/rust/rust-dp-observer/</guid><description>&lt;p&gt;The Observer pattern is where Rust&amp;rsquo;s ownership model gets &lt;em&gt;really&lt;/em&gt; opinionated. In C# or Java, Observer is simple — maintain a list of listeners, call a method on each when something happens. But &amp;ldquo;a list of mutable references to objects that can be called at any time&amp;rdquo; is basically everything Rust&amp;rsquo;s borrow checker exists to prevent. So how do you do event-driven programming in Rust? You have more options than you&amp;rsquo;d think, and some of them are better than what OOP languages offer.&lt;/p&gt;</description></item><item><title>Lesson 2: Strategy Pattern — Trait objects and generics</title><link>/post/rust/rust-dp-strategy/</link><pubDate>Fri, 03 Oct 2025 14:15:00 +0000</pubDate><guid>/post/rust/rust-dp-strategy/</guid><description>&lt;p&gt;In my first real Go project, I wrote an interface for a payment processor. Two implementations — Stripe and PayPal. Simple polymorphism. When I tried the same thing in Rust, the compiler hit me with a wall of errors about &lt;code&gt;dyn&lt;/code&gt;, &lt;code&gt;Box&lt;/code&gt;, object safety, and sized types. It took me a full afternoon to understand what was happening. The Strategy pattern — which is trivial in most OOP languages — forced me to actually understand Rust&amp;rsquo;s type system. And I came out the other side a better programmer for it.&lt;/p&gt;</description></item><item><title>Lesson 1: Builder Pattern — Typestate builders and compile-time validation</title><link>/post/rust/rust-dp-builder-advanced/</link><pubDate>Wed, 01 Oct 2025 08:30:00 +0000</pubDate><guid>/post/rust/rust-dp-builder-advanced/</guid><description>&lt;p&gt;I once spent three days debugging a production outage caused by a builder that silently accepted a missing &lt;code&gt;host&lt;/code&gt; field and defaulted to &lt;code&gt;localhost&lt;/code&gt;. In Java. The builder compiled fine, the tests passed — because they ran against localhost — and the deployment connected to nothing. That was the day I stopped trusting optional fields in builders.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s type system lets you make that entire category of bug impossible. Not at runtime. Not with validation methods. At &lt;em&gt;compile time&lt;/em&gt;.&lt;/p&gt;</description></item></channel></rss>