<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged patterns</title>
<link>https://coderlegion.com/tag/patterns</link>
<description>Powered by Question2Answer</description>
<item>
<title>Platform Engineering’s patterns and anti-patterns</title>
<link>https://coderlegion.com/11829/platform-engineerings-patterns-and-anti-patterns</link>
<description>&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/devops/platform-engineering/&quot;&gt;Platform Engineering&lt;/a&gt; has been around for a while. Interest recently accelerated, however, thanks to its relevance to DevOps.&lt;/p&gt;
&lt;p&gt;For some, Platform Engineering solves a common DevOps Topologies’ problem: Embedding IT operations into DevOps teams that lack time and experience to do it well. This is an anti-pattern, also known as &lt;a rel=&quot;nofollow&quot; href=&quot;https://web.devopstopologies.com/#anti-type-f&quot;&gt;Anti-Type F&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Platform Engineering can help you scale the organization and reduce developer overload.&lt;/p&gt;
&lt;p&gt;Use our &lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/devops/platform-engineering/when-to-adopt-platform-engineering/&quot;&gt;Platform Engineering decision flow&lt;/a&gt; to find out if it’s the right solution to your problems. It also guides you to put key measurements in place early so you can track the value of your internal developer platform (IDP).&lt;/p&gt;
&lt;p&gt;We describe the anti-patterns and positive patterns of Platform Engineering below. Let us know if you have a suggestion.&lt;/p&gt;
&lt;h3 id=&quot;platform-engineering-anti-patterns&quot;&gt;Platform Engineering anti-patterns&lt;/h3&gt;
&lt;p&gt;If familiar with Team Topologies, you’re less likely to use one of the common Platform Engineering anti-patterns. The library can still be a helpful way to describe problems to your colleagues when you find trouble.&lt;/p&gt;
&lt;h4 id=&quot;complex-platforms&quot;&gt;Complex platforms&lt;/h4&gt;
&lt;p&gt;An IDP should reduce complexity and remove cognitive overload for developers. If the platform is too difficult to use, it failed at a crucial goal. There are several ways a platform can add to a developer’s burden:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses a configuration file format that isn’t familiar to the developer&lt;/li&gt;
&lt;li&gt;APIs don’t have a consistent convention for methods and parameters&lt;/li&gt;
&lt;li&gt;There’s no documentation&lt;/li&gt;
&lt;li&gt;The platform doesn’t reduce developer pain&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have configuration files as part of your platform, be flexible with the format. It should be trivial to parse the same data from a JSON file from web developers and a YAML or XML file from back-end teams. This means the developers can use a format familiar to them rather than one convenient to you.&lt;/p&gt;
&lt;p&gt;When creating an API or command line interface (CLI), being meticulous and consistent with method names, parameter names, and the order of parameters can make it easier to use.&lt;/p&gt;
&lt;p&gt;Your IDP documentation should be concise and clear, focusing on what’s available and how to use it.&lt;/p&gt;
&lt;p&gt;Where developers don’t meet governance, risk, and compliance (GRC) objectives, adding a platform that handles these needs adds more load to development teams. Solving existing developer problems first allows you to solve external requirements without burdening developers.&lt;/p&gt;
&lt;p&gt;If your IDP doesn’t significantly reduce developer cognitive overload, complexity, and burnout, it doesn’t work. It also fails if it transfers all the pain to the platform team.&lt;/p&gt;
&lt;h4 id=&quot;-field-of-dreams-platforms&quot;&gt;”Field of dreams” platforms&lt;/h4&gt;
&lt;p&gt;In the 1989 movie &lt;em&gt;Field of Dreams&lt;/em&gt;, a voice tells Ray Kinsella (Kevin Costner) that “If you build it, they will come.” In the film, building a baseball park in a cornfield reunites Kinsella with his father and produces a stream of fans coming to watch games at the farm.&lt;/p&gt;
&lt;p&gt;If you take the same approach when building your internal developer platform, you’ll struggle to gain internal market share. You need to involve developers from the start and build a platform that solves their specific problems.&lt;/p&gt;
&lt;p&gt;If you build the platform before talking to its potential customers, you find yourself with a product that doesn’t fit your market. Some symptoms include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Poor technology fit&lt;/strong&gt;: Making it easier to manage Kubernetes only to find developers moved to serverless functions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skill assumptions&lt;/strong&gt;: Creating a simplified template for microservices only to find developers are microservice experts and don’t value your solution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forcing choices&lt;/strong&gt;: Designing a platform with the latest and greatest technology and finding the business doesn’t want to move from stable solutions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can’t design your IDP on faith alone. You must talk to developers across many teams to find common pain points your platform could remove. Creating a platform in isolation guarantees low adoption rates.&lt;/p&gt;
&lt;h4 id=&quot;the-skill-concentration-trap&quot;&gt;The skill concentration trap&lt;/h4&gt;
&lt;p&gt;When you create a Platform Engineering team, you’ll likely add people with the most experience in areas the platform will cover. This can cause a problem when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The platform team thinks they know better than the development teams&lt;/li&gt;
&lt;li&gt;Development teams lose the knowledge they need to run their software&lt;/li&gt;
&lt;li&gt;Development teams lose the people who could migrate them onto a platform&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Suppose you move people from development teams to create a platform team. You need a strong succession plan and a greater focus on collaboration to upskill the development teams for self-service. You must ensure you don’t create a cognitive overload and burnout by removing skilled team members.&lt;/p&gt;
&lt;p&gt;If people leaving the teams were the only ones who cared about operations, the remaining members might see little benefit to the platform. It would be a disaster if developers ignored operations tasks.&lt;/p&gt;
&lt;p&gt;You may need to start your platform team with fewer people. You can then solve problems that make it easier for teams to release more people. Choosing platform team members on communication and collaboration skills, and not just technical knowledge, increases the chances of success.&lt;/p&gt;
&lt;h4 id=&quot;magpie-platforms&quot;&gt;Magpie platforms&lt;/h4&gt;
&lt;p&gt;People attracted to shiny objects are often called magpies. That’s because members of the crow family tend to take shiny objects belonging to their owners when kept in captivity. A magpie platform focuses on shiny new technologies and not solving problems developers face with existing systems.&lt;/p&gt;
&lt;p&gt;Building a platform to support a greenfield project is attractive, but there are far better ways. You make more impact by helping teams with their existing production software.&lt;/p&gt;
&lt;p&gt;You build a better platform for the new software once you can identify the areas where developers struggle. When a development team adopts new technology, it can look like they need help with everything. You should let them establish their knowledge for the actual pain points to emerge. Many of their early pains will disappear when they get used to the new tech.&lt;/p&gt;
&lt;p&gt;You need to prove the platform’s impact, so a stable starting point is necessary for others to take your measurements seriously. Stakeholders may dismiss your improvements if they think it’s due to teams getting familiar with the technology and business domain.&lt;/p&gt;
&lt;p&gt;Platforms must prove their impact on existing teams and the tech stacks they use. There will be many opportunities to merge technologies where teams solved a problem differently. Sometimes introducing a new tool for deployment or monitoring can remove pain from many teams at once. It’s crucial you can show the platform’s benefits.&lt;/p&gt;
&lt;h4 id=&quot;under-invested-platforms&quot;&gt;Under-invested platforms&lt;/h4&gt;
&lt;p&gt;Once you’ve introduced a platform into your organization, it needs long-term investment to keep it usable. When a platform team disbands after delivering a product, the platform can become an anchor that adds drag to all who depend on it.&lt;/p&gt;
&lt;p&gt;The context that led to the decisions you baked into the IDP platform can change, leaving teams stuck with old choices that no longer work. The platform will become so difficult to use that teams must migrate away from the platform to get their work done.&lt;/p&gt;
&lt;p&gt;Platforms need to continually improve. You need to add new golden pathways and phase others out to ensure the platform continues to benefit the business.&lt;/p&gt;
&lt;p&gt;You should limit the platform’s ambition to match the long-term investment levels.&lt;/p&gt;
&lt;h4 id=&quot;stretched-platforms&quot;&gt;Stretched platforms&lt;/h4&gt;
&lt;p&gt;If your organization has a fragmented technology landscape, it can be tempting to solve too many problems with the IDP. Suppose you support all existing tools and technology. In that case, the platform will become stretched too thin to be useful. Your platform team will be even more overloaded than the developers were.&lt;/p&gt;
&lt;p&gt;A common cause of stretched platforms is measuring the platform team on internal market share. Adding more golden pathways is one way to increase market share, as the platform can help more teams. However, this defeats the goal of using the pathways to encourage consolidation around good technology choices.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/devops/metrics/monk-metrics/&quot;&gt;MONK metrics&lt;/a&gt; can help balance market share with developer satisfaction and outcomes. This can help you avoid the problem of stretched platforms.&lt;/p&gt;
&lt;h4 id=&quot;other-signs-of-trouble&quot;&gt;Other signs of trouble&lt;/h4&gt;
&lt;p&gt;Alongside common anti-patterns, you should watch for other signs of trouble. Common gaps can open when an IDP becomes available.&lt;/p&gt;
&lt;h4 id=&quot;self-service-cost-problems&quot;&gt;Self-service cost problems&lt;/h4&gt;
&lt;p&gt;When you make a platform with self-service options, you must ensure a strong story around how you track and control cost. If every developer spins up production-like environments, costs could rise out of control.&lt;/p&gt;
&lt;p&gt;Any button that makes spending easy should include a feature to limit that spending automatically. This means test environments have short lifespans, and there’s a cap on concurrent environments.&lt;/p&gt;
&lt;p&gt;There’s an opportunity for the platform team to bridge with the finance team on this subject.&lt;/p&gt;
&lt;h4 id=&quot;general-purpose-devops-platforms&quot;&gt;General-purpose DevOps platforms&lt;/h4&gt;
&lt;p&gt;When platform decisions happen away from technical knowledge, there’s a temptation to try an all-in-one DevOps platform to solve all problems.&lt;/p&gt;
&lt;p&gt;This doesn’t align with Platform Engineering or DevOps. If you buy a single general-purpose tool, you limit the platform’s capability and your continuous improvement abilities. The features of the tool constrain the solution for all problems.&lt;/p&gt;
&lt;p&gt;Instead, you should design the platform as an internal product that solves specific pain points of development teams. Rather than opting for general-purpose tools, select best-in-class tools where you need them. You’re also likely to create reusable libraries that make it easier for developers to align with unified logging and telemetry design.&lt;/p&gt;
&lt;p&gt;A golden pathway is not a tool, though it makes sense to increase the platform’s capability by leaning into specialist tools for builds, deployments, and monitoring.&lt;/p&gt;
&lt;h4 id=&quot;flexibility-has-two-sharp-handles&quot;&gt;Flexibility has two sharp handles&lt;/h4&gt;
&lt;p&gt;While platform teams aim to help development teams move towards a standard landscape, edge cases will always exist. A rigid platform will force development teams to exit and find alternatives. Talking with teams and listening for edge cases allows platform teams to counter edge cases and keep internal market share.&lt;/p&gt;
&lt;p&gt;Equally, accommodating every scenario without any consolidation into golden pathways causes other problems. It could swamp the platform team with the complexity and cognitive load that Platform Engineering should remove. The best software products have a clear focus. They don’t try to do everything.&lt;/p&gt;
&lt;p&gt;You should design the platform to allow teams to add edge cases without necessarily bringing the edge cases into support. Edge cases shouldn’t force people off the platform or increase the platform’s surface.&lt;/p&gt;
&lt;p&gt;Don’t attempt to support every tech stack in the business. When you report on platform market share, highlight:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total market size (all developers)&lt;/li&gt;
&lt;li&gt;Size of the supported market (all developers using a tech stack supported by a golden path)&lt;/li&gt;
&lt;li&gt;Market share (the number of developers using the platform)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10012637735400595632&quot; alt=&quot;Potential market and developers on IDP shown on an area chart&quot;&gt;&lt;/p&gt;
&lt;p&gt;The market share chart shows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The total size of the internal developer market&lt;/li&gt;
&lt;li&gt;How much of the market could use the internal developer platform&lt;/li&gt;
&lt;li&gt;How many chose to do so.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can use the chart to guide decisions for new golden pathways and track the platform’s appeal to its intended audience.&lt;/p&gt;
&lt;h4 id=&quot;platforms-can-t-solve-culture&quot;&gt;Platforms can’t solve culture&lt;/h4&gt;
&lt;p&gt;It’s easy to create a team. Building the interactions that make them successful is far more work. Team Topologies describe this using a combination of team and interaction modes.&lt;/p&gt;
&lt;p&gt;If you create a platform team without paying attention to the design of interactions, it will fail.&lt;/p&gt;
&lt;p&gt;Software delivery requires a high-trust, low-blame culture to succeed. Platform Engineering is no different. Culture is one of the biggest predictors of performance. Find out more about &lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/devops/culture/&quot;&gt;DevOps culture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Building a platform isn’t a project. If you create an IDP, it must be a long-term strategy. When a platform is &lt;em&gt;on-hold&lt;/em&gt; it limits the performance for all its users. This is worse than not creating a platform in the first place.&lt;/p&gt;
&lt;p&gt;You need solid team design, clear interaction modes, and a platform as a product approach to build a good IDP.&lt;/p&gt;
&lt;h3 id=&quot;how-to-do-platform-engineering-well&quot;&gt;How to do Platform Engineering well&lt;/h3&gt;
&lt;p&gt;Now you’ve seen many ways Platform Engineering can fail, it’s time to look at what success looks like.&lt;/p&gt;
&lt;p&gt;In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.puppet.com/resources/state-of-platform-engineering&quot;&gt;Puppet State of Platform Engineering Report&lt;/a&gt;, 94% of respondents found the concept helped the organization realize the benefits of DevOps. If you have strong performance against the &lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/devops/metrics/dora-metrics/&quot;&gt;DORA Metrics&lt;/a&gt;, you’re likely to succeed with Platform Engineering too.&lt;/p&gt;
&lt;h4 id=&quot;the-internal-developer-platform-is-a-product&quot;&gt;The internal developer platform is a product&lt;/h4&gt;
&lt;p&gt;The most critical sign of good Platform Engineering is that it’s run as a product. Developers are customers, and the platform should solve their problems to win internal market share.&lt;/p&gt;
&lt;p&gt;If doing this well, you’ll have regular contact with developers, and measurable &lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/devops/metrics/monk-metrics/&quot;&gt;platform success criteria&lt;/a&gt;. If you have a product manager, the don’t only maintain a backlog. They should raise awareness of the platform and network with other departments to incorporate their needs. They help developers satisfy security, governance, risk, and compliance needs.&lt;/p&gt;
&lt;p&gt;The platform needs a strong vision that inspires good decision-making. The platform shouldn’t support bad choices to gain internal market share. The product manager should capture decisions to refer to later on. It’s common for good decisions to get reversed when everyone’s forgotten the reason. A register of decisions can prevent mistakes.&lt;/p&gt;
&lt;p&gt;By treating the platform as a product, you must ensure documentation quality stays high. High-quality documentation is crucial to product adoption.&lt;/p&gt;
&lt;p&gt;Having a product-based approach and running a competitive product are vital signs of good Platform Engineering.&lt;/p&gt;
&lt;h4 id=&quot;use-devops-to-build-the-platform&quot;&gt;Use DevOps to build the platform&lt;/h4&gt;
&lt;p&gt;DevOps combines the following to increase software delivery performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Transformational leadership&lt;/li&gt;
&lt;li&gt;Lean product management&lt;/li&gt;
&lt;li&gt;Continuous Delivery&lt;/li&gt;
&lt;li&gt;Organizational culture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The same capabilities will increase the likelihood of creating a valuable platform.&lt;/p&gt;
&lt;p&gt;You don’t need to create a fully formed golden pathway immediately. Instead, solve 1 wide-reaching problem and iterate to success.&lt;/p&gt;
&lt;p&gt;A typical pattern is to make it easy to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deploy common approved tech stacks&lt;/li&gt;
&lt;li&gt;Provision infrastructure and create environments&lt;/li&gt;
&lt;li&gt;Collect telemetry and error logs and track systems in Production&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The underlying pattern is to find and abstract a complex area so the development team doesn’t get lost in the weeds. In some cases, an off-the-shelf tool will help with this. In other cases, the platform team might create a facade to simplify a tricky area.&lt;/p&gt;
&lt;p&gt;The goal should be to start with the thinnest viable platform that supports developers where they most need it. It’s better to expand features slowly. Pick the areas with the most significant impact rather than trying to solve all problems.&lt;/p&gt;
&lt;p&gt;For example, if your toolset has large JSON, YAML, and XML configuration sets, you could provide a unified format offering fewer options. Developers then only need to adjust 1 small file in a single format instead of many large files in different formats. You should provide considered default options for all the settings you don’t expose through the platform.&lt;/p&gt;
&lt;p&gt;Another popular option is putting different tools and workflows behind a simple API for developers.&lt;/p&gt;
&lt;h4 id=&quot;make-good-build-or-buy-decisions&quot;&gt;Make good build or buy decisions&lt;/h4&gt;
&lt;p&gt;Successful platform teams know when to build something from scratch and when to bring in a specific tool for the job. Many best-in-class tools for Continuous Integration, deployment automation, and monitoring offer features to the IDP.&lt;/p&gt;
&lt;p&gt;It would be best to avoid general-purpose solutions that try to do everything, as they can become a limiting factor. A crucial platform capability is swapping out a tool without creating work for developers. Imagine changing your event logging tool. Without Platform Engineering, it may involve a list of applications to update and schedule many similar changes. The internal developer platform should lower the cost to change this by managing the swap behind the platform API.&lt;/p&gt;
&lt;p&gt;No matter how you compose off-the-shelf and custom components, aim to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Keep the developer in their preferred place, usually a text editor or IDE.&lt;/li&gt;
&lt;li&gt;Make everything work seamlessly together&lt;/li&gt;
&lt;li&gt;Provide snap-in solutions to common problems (authentication, logging, monitoring, for example)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A team that brings in specialized tools will create a more robust platform faster than when building everything from scratch.&lt;/p&gt;
&lt;h4 id=&quot;create-the-network&quot;&gt;Create the network&lt;/h4&gt;
&lt;p&gt;When developers adopt your platform, they should also become part of your customer network. The teams using the platform can help each other when they get stuck. Your long-term customers can share how they have used the platform to solve their problems. That can inspire other developers to adopt or extend their use of the IDP.&lt;/p&gt;
&lt;p&gt;You can also create opportunities for different teams to discuss typical pain points the platform could solve.&lt;/p&gt;
&lt;h4 id=&quot;run-like-open-source&quot;&gt;Run like open source&lt;/h4&gt;
&lt;p&gt;Let the platform’s users contribute to it; they are all developers, after all! This is ‘inner sourcing.’&lt;/p&gt;
&lt;p&gt;This means more than accepting code contributions. It also means you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Publish contribution guidelines&lt;/li&gt;
&lt;li&gt;Keep contribution documentation up to date&lt;/li&gt;
&lt;li&gt;Think about how a suggested change may impact other customers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find more ideas at &lt;a rel=&quot;nofollow&quot; href=&quot;https://innersourcecommons.org/&quot;&gt;InnerSource Commons&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;keep-an-eye-on-progress&quot;&gt;Keep an eye on progress&lt;/h4&gt;
&lt;p&gt;You need to track the platform’s impact to inform product management and ensure continued investment.&lt;/p&gt;
&lt;p&gt;The MONK metrics are an excellent place to start. The MONK metrics are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Market share&lt;/li&gt;
&lt;li&gt;Onboarding time&lt;/li&gt;
&lt;li&gt;Net Promoter Score (NPS)&lt;/li&gt;
&lt;li&gt;Key customer metrics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read more about the &lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/devops/metrics/monk-metrics/&quot;&gt;Platform Engineering MONK metrics&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;There are several anti-patterns as well a strong pattern of successful Platform Engineering.&lt;/p&gt;
&lt;p&gt;You’ll be more likely to succeed if you run the IDP as a product with lean product management techniques. You should capture the positive outcomes development teams achieve to encourage long-term funding from the organization.&lt;/p&gt;
&lt;p&gt;Platform Engineering solves a particular set of problems. Team topologies provide many ways to design your team structures if Platform Engineering isn’t the right approach for you.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11829/platform-engineerings-patterns-and-anti-patterns</guid>
<pubDate>Tue, 17 Feb 2026 10:22:51 +0000</pubDate>
</item>
<item>
<title>State Pattern: Transforming Objects Through Internal States</title>
<link>https://coderlegion.com/2911/state-pattern-transforming-objects-through-internal-states</link>
<description>&lt;h2 id=&quot;state-pattern-transforming-objects-through-internal-states&quot;&gt;State Pattern: Transforming Objects Through Internal States&lt;/h2&gt;
&lt;p&gt;In software development, we often encounter objects whose behavior must change based on their internal state. Think of a document (draft, under review, published), an order (new, paid, shipped, delivered), or as we'll explore, a student moving through academic stages.&lt;/p&gt;
&lt;p&gt;However, with the advancement of programming and the complexity of the problems we face, we discovered that every object has two types of attributes: &quot;static attributes&quot; and &quot;state attributes,&quot; which will be the focus of our discussion now. Because these attributes have behavior and state rather than a fixed value, dealing with them can be complex. Changing an object's state may result in a change in its behavior. For example, a person's behavior when they were children differs from their behavior when they became adolescents, even though they remain the same &quot;human.&quot; &lt;/p&gt;
&lt;p&gt;Therefore, it was found preferable to isolate these attributes in an abstract interface. Within the abstract interface, there are functions known as &quot;state functions,&quot; which cause states to change and are similar to rules. In some cases, they are called &quot;state operator functions.&quot; Each state then executes these functions, but according to the implementation appropriate for that state.&lt;/p&gt;
&lt;p&gt;When handled improperly, state-dependent behavior leads to complex, difficult-to-maintain code filled with conditional statements. The State Pattern offers an elegant solution to this problem.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-the-state-pattern-&quot;&gt;What is the State Pattern?&lt;/h3&gt;
&lt;p&gt;The State Pattern allows an object to change its behavior when its internal state changes. The pattern makes the object appear to change its class by encapsulating state-specific behavior in separate state classes and eliminating complex conditional statements.&lt;/p&gt;
&lt;p&gt;As defined by the Gang of Four, this pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Encapsulates state-specific behavior in separate classes&lt;/li&gt;
&lt;li&gt;Delegates state-specific behavior to the current state object&lt;/li&gt;
&lt;li&gt;Allows for easy addition of new states with minimal changes to existing code&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-problem-evolving-student-states&quot;&gt;The Problem: Evolving Student States&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;http://&quot; alt=&quot;&quot;&gt;&lt;br&gt;
Let's consider a concrete example: a &lt;code&gt;Student&lt;/code&gt; class that handles different behaviors as the student progresses through exam preparation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Preparing&lt;/strong&gt;: Student is studying for the exam&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExamTaken&lt;/strong&gt;: Student has taken the exam and is awaiting results&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Passed&lt;/strong&gt;: Student has passed and completed the course&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;traditional-approach-with-conditionals&quot;&gt;Traditional Approach with Conditionals&lt;/h4&gt;
&lt;p&gt;Here's how this might be implemented traditionally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Student {
    // State constants
    public static final int PREPARING = 0;
    public static final int EXAM_TAKEN = 1;
    public static final int PASSED = 2;

    private int state;
    private String name;

    public Student(String name) {
        this.name = name;
        // Initial state is PREPARING
        this.state = PREPARING;
    }

    public void study() {
        System.out.println(name + &quot; is attempting to study.&quot;);

        if (state == PREPARING) {
            System.out.println(&quot;Studying harder to be prepared for the exam!&quot;);
        } else if (state == EXAM_TAKEN) {
            System.out.println(&quot;Already took the exam! Waiting for results...&quot;);
        } else if (state == PASSED) {
            System.out.println(&quot;Studying for the next course!&quot;);
        } else {
            System.out.println(&quot;Unknown state!&quot;);
        }
    }

    public void takeExam() {
        System.out.println(name + &quot; is attempting to take the exam.&quot;);

        if (state == PREPARING) {
            System.out.println(&quot;Taking the exam now...&quot;);
            state = EXAM_TAKEN;
        } else if (state == EXAM_TAKEN) {
            System.out.println(&quot;Cannot take the exam again! Already taken.&quot;);
        } else if (state == PASSED) {
            System.out.println(&quot;Already passed this exam! Moving to next course.&quot;);
        } else {
            System.out.println(&quot;Unknown state!&quot;);
        }
    }

    public void receiveResults(boolean passed) {
        System.out.println(name + &quot; is receiving exam results.&quot;);

        if (state == PREPARING) {
            System.out.println(&quot;Cannot receive results yet - haven't taken the exam!&quot;);
        } else if (state == EXAM_TAKEN) {
            System.out.println(&quot;Receiving exam results...&quot;);
            if (passed) {
                state = PASSED;
                System.out.println(&quot;Passed the exam!&quot;);
            } else {
                state = PREPARING;
                System.out.println(&quot;Failed the exam. Need to prepare again!&quot;);
            }
        } else if (state == PASSED) {
            System.out.println(&quot;Already received the results. Passed!&quot;);
        } else {
            System.out.println(&quot;Unknown state!&quot;);
        }
    }

    public String getStateName() {
        if (state == PREPARING) {
            return &quot;Preparing for exam&quot;;
        } else if (state == EXAM_TAKEN) {
            return &quot;Waiting for results&quot;;
        } else if (state == PASSED) {
            return &quot;Passed the exam&quot;;
        } else {
            return &quot;Unknown&quot;;
        }
    }

    @Override
    public String toString() {
        return name + &quot; - Current state: &quot; + getStateName();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;problems-with-the-traditional-approach&quot;&gt;Problems with the Traditional Approach&lt;/h4&gt;
&lt;p&gt;This implementation has several issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Code Duplication&lt;/strong&gt;: The same state checks are repeated in every method&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex Conditionals&lt;/strong&gt;: Each method contains multiple if-else branches&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scattered State Transitions&lt;/strong&gt;: State changes are dispersed throughout the code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Poor Maintainability&lt;/strong&gt;: Adding a new state requires modifying every method&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error Prone&lt;/strong&gt;: Easy to miss updating a condition when adding states&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hard to Read&lt;/strong&gt;: Understanding all possible state transitions is difficult&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Violation of OCP&lt;/strong&gt;: The class must be modified to add new states&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Violation of SRP&lt;/strong&gt;: The Student class is responsible for all state behaviors&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;the-solution-applying-the-state-pattern&quot;&gt;The Solution: Applying the State Pattern&lt;/h3&gt;
&lt;p&gt;Instead of managing state transitions with conditionals, we can use the State Pattern to separate each state's behavior into its own class:&lt;/p&gt;
&lt;h4 id=&quot;1-define-the-state-interface&quot;&gt;1. Define the State Interface&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;interface StudentState {
    void study(Student student);
    void takeExam(Student student);
    void receiveResults(Student student, boolean passed);
    String getStateName();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-implement-concrete-states&quot;&gt;2. Implement Concrete States&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class PreparingState implements StudentState {
    @Override
    public void study(Student student) {
        System.out.println(&quot;Studying harder to be prepared for the exam!&quot;);
    }

    @Override
    public void takeExam(Student student) {
        System.out.println(&quot;Taking the exam now...&quot;);
        student.setState(new ExamTakenState());
    }

    @Override
    public void receiveResults(Student student, boolean passed) {
        System.out.println(&quot;Cannot receive results yet - haven't taken the exam!&quot;);
    }

    @Override
    public String getStateName() {
        return &quot;Preparing for exam&quot;;
    }
}

class ExamTakenState implements StudentState {
    @Override
    public void study(Student student) {
        System.out.println(&quot;Already took the exam! Waiting for results...&quot;);
    }

    @Override
    public void takeExam(Student student) {
        System.out.println(&quot;Cannot take the exam again! Already taken.&quot;);
    }

    @Override
    public void receiveResults(Student student, boolean passed) {
        System.out.println(&quot;Receiving exam results...&quot;);
        if (passed) {
            student.setState(new PassedState());
            System.out.println(&quot;Passed the exam!&quot;);
        } else {
            student.setState(new PreparingState());
            System.out.println(&quot;Failed the exam. Need to prepare again!&quot;);
        }
    }

    @Override
    public String getStateName() {
        return &quot;Waiting for results&quot;;
    }
}

class PassedState implements StudentState {
    @Override
    public void study(Student student) {
        System.out.println(&quot;Studying for the next course!&quot;);
    }

    @Override
    public void takeExam(Student student) {
        System.out.println(&quot;Already passed this exam! Moving to next course.&quot;);
    }

    @Override
    public void receiveResults(Student student, boolean passed) {
        System.out.println(&quot;Already received the results. Passed!&quot;);
    }

    @Override
    public String getStateName() {
        return &quot;Passed the exam&quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-refactor-the-context-class&quot;&gt;3. Refactor the Context Class&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Student {
    private StudentState state;
    private String name;

    public Student(String name) {
        this.name = name;
        // Initial state is Preparing
        this.state = new PreparingState();
    }

    public void setState(StudentState state) {
        this.state = state;
    }

    public void study() {
        System.out.println(name + &quot; is attempting to study.&quot;);
        state.study(this);
    }

    public void takeExam() {
        System.out.println(name + &quot; is attempting to take the exam.&quot;);
        state.takeExam(this);
    }

    public void receiveResults(boolean passed) {
        System.out.println(name + &quot; is receiving exam results.&quot;);
        state.receiveResults(this, passed);
    }

    public String getStateName() {
        return state.getStateName();
    }

    @Override
    public String toString() {
        return name + &quot; - Current state: &quot; + state.getStateName();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;benefits-of-the-state-pattern&quot;&gt;Benefits of the State Pattern&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Clean Code&lt;/strong&gt;: Eliminates messy conditionals&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: State-specific code is localized&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;: New states can be added easily&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: States can be swapped at runtime&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testability&lt;/strong&gt;: Each state can be tested in isolation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Readability&lt;/strong&gt;: State transitions are explicit&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single Responsibility&lt;/strong&gt;: Each state class has one purpose&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open-Closed Principle&lt;/strong&gt;: Student class is closed for modification but open for extension&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;state-pattern-vs-strategy-pattern-key-differences&quot;&gt;State Pattern vs. Strategy Pattern: Key Differences&lt;/h3&gt;
&lt;p&gt;The State and Strategy patterns share a similar structure but have different intents:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Aspect &lt;/th&gt;&lt;th&gt;Strategy Pattern &lt;/th&gt;&lt;th&gt;State Pattern&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Intent &lt;/td&gt;&lt;td&gt;Defines a family of interchangeable algorithms &lt;/td&gt;&lt;td&gt;Changes object behavior when internal state changes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Control &lt;/td&gt;&lt;td&gt;Client code selects strategy &lt;/td&gt;&lt;td&gt;States often control transitions to other states&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Relationship &lt;/td&gt;&lt;td&gt;Strategies typically independent &lt;/td&gt;&lt;td&gt;States often know about and reference each other&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Example &lt;/td&gt;&lt;td&gt;Client decides duck's flying behavior &lt;/td&gt;&lt;td&gt;Student automatically changes own state&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;when-to-use-the-state-pattern&quot;&gt;When to Use the State Pattern&lt;/h3&gt;
&lt;p&gt;The State Pattern is particularly useful when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An object's behavior depends on its state&lt;/li&gt;
&lt;li&gt;You have multiple operations with state-dependent behavior&lt;/li&gt;
&lt;li&gt;State transitions follow well-defined rules&lt;/li&gt;
&lt;li&gt;You need to eliminate complex conditional logic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It might be overkill when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have few states or simple transitions&lt;/li&gt;
&lt;li&gt;States rarely change&lt;/li&gt;
&lt;li&gt;State-specific behavior is minimal&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;real-world-examples&quot;&gt;Real-World Examples&lt;/h3&gt;
&lt;p&gt;The State Pattern appears in many modern frameworks and libraries:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;XState&lt;/strong&gt;: JavaScript library implementing formal state machines to manage complex UI state transitions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RxJava&lt;/strong&gt;: Uses Observer pattern primarily but incorporates state management for asynchronous stream processing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unidraw&lt;/strong&gt;: Framework for graphical editors that employs state-like behavior for tool switching&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The State Pattern is a powerful tool for managing complex state-dependent behavior. By encapsulating each state in its own class, you create code that's easier to understand, test, and extend. Next time you find yourself writing a growing number of state-checking conditionals, consider if the State Pattern might provide a more elegant solution.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This article is inspired by concepts from &quot;Head First Design Patterns&quot; by Eric Freeman &amp;amp; Elisabeth Robson and &quot;Design Patterns: Elements of Reusable Object-Oriented Software&quot; by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Setup Guide</category>
<guid isPermaLink="true">https://coderlegion.com/2911/state-pattern-transforming-objects-through-internal-states</guid>
<pubDate>Wed, 07 May 2025 21:42:59 +0000</pubDate>
</item>
<item>
<title>The State Pattern</title>
<link>https://coderlegion.com/742/the-state-pattern</link>
<description>&lt;h2 id=&quot;understanding-the-state-pattern-with-calculator&quot;&gt;Understanding the State Pattern with Calculator&lt;/h2&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;The State Pattern allows an object to change its behavior dynamically based on its internal state. It’s a powerful design pattern for handling complex, state-dependent behavior in an organized and maintainable way.&lt;/p&gt;
&lt;p&gt;In this article, we’ll explore the State Pattern by implementing a simple Calculator with multiple states.&lt;/p&gt;
&lt;h3 id=&quot;calculator-state-example&quot;&gt;Calculator State Example&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1️⃣ State Interface&lt;/strong&gt;&lt;br&gt;
The State interface defines the contract that all concrete state classes must implement. It contains methods that represent the state-specific behavior.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface ICalculatorState {
    void EnterNumber(CalculatorContext context, int number);
    void PerformOperation(CalculatorContext context, string operation);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2️⃣ Concrete States&lt;/strong&gt;&lt;br&gt;
Concrete states implement the State interface. Each state encapsulates the behavior for a specific condition of the Context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NumberState: Handles behavior when the calculator expects a number.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class NumberState : ICalculatorState {
    public void EnterNumber(CalculatorContext context, int number) {
        context.CurrentValue = number;
        Console.WriteLine($&quot;Number entered: {number}&quot;);
    }

    public void PerformOperation(CalculatorContext context, string operation) {
        Console.WriteLine(&quot;Operation not allowed in Number State.&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;OperationState: Handles behavior when the calculator performs an operation.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class OperationState : ICalculatorState {
    public void EnterNumber(CalculatorContext context, int number) {
        context.SecondValue = number;
        Console.WriteLine($&quot;Second number entered: {number}&quot;);
    }

public void PerformOperation(CalculatorContext context, string operation) {
        switch (operation) {
            case &quot;+&quot;:
                context.Result = context.CurrentValue + context.SecondValue;
                break;
            case &quot;-&quot;:
                context.Result = context.CurrentValue - context.SecondValue;
                break;
            default:
                Console.WriteLine(&quot;Unsupported operation.&quot;);
                return;
        }
        Console.WriteLine($&quot;Operation performed: {context.Result}&quot;);
        context.SetState(new NumberState()); // Transition back to NumberState
    }
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 3️⃣ Context: The Context is the core class that ties everything together. It keeps track of the current state and delegates behavior to the current state object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; public class CalculatorContext {
        private ICalculatorState _state;
        public int CurrentValue { get; set; }
        public int SecondValue { get; set; }
        public int Result { get; set; }
    
        public CalculatorContext() {
            _state = new NumberState(); // Start in the NumberState
        }
    
        public void SetState(ICalculatorState state) {
            _state = state;
        }
    
        public void EnterNumber(int number) {
            _state.EnterNumber(this, number);
        }
    
        public void PerformOperation(string operation) {
            _state.PerformOperation(this, operation);
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4️⃣ Main Program&lt;br&gt;
Here’s how it all comes together in the main program:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Program {
    static void Main(string[] args) {
        CalculatorContext calculator = new CalculatorContext();

        calculator.EnterNumber(10);
        calculator.SetState(new OperationState());
        calculator.EnterNumber(20);
        calculator.PerformOperation(&quot;+&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;output-&quot;&gt;Output :&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Number entered: 10
Second number entered: 20
Operation performed: 30
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;real-world-applications-of-the-state-pattern&quot;&gt;Real-World Applications of the State Pattern&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ATM Machine:&lt;/strong&gt; Handling Idle, Processing, and Dispensing states.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Media Player:&lt;/strong&gt; Play, Pause, and Stop states.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Order Processing:&lt;/strong&gt; Pending, Shipped, and Delivered states.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Traffic Light System:&lt;/strong&gt; Red, Yellow, and Green states.&lt;/p&gt;
&lt;h2 id=&quot;common-pitfalls-amp-best-practices&quot;&gt;Common Pitfalls &amp;amp; Best Practices&lt;/h2&gt;
&lt;h3 id=&quot;-best-practices&quot;&gt;✅ Best Practices&lt;/h3&gt;
&lt;p&gt;Keep states separate and self-contained.&lt;/p&gt;
&lt;p&gt;Use Factory Patterns to manage state transitions dynamically.&lt;/p&gt;
&lt;p&gt;Apply lazy initialization to avoid unnecessary memory usage.&lt;/p&gt;
&lt;h3 id=&quot;-common-mistakes&quot;&gt;⚠ Common Mistakes&lt;/h3&gt;
&lt;p&gt;Overcomplicating simple problems with the State Pattern.&lt;/p&gt;
&lt;p&gt;Not handling undefined states, leading to unexpected behaviors.&lt;/p&gt;
&lt;p&gt;Using too many states, which can make maintenance difficult.&lt;/p&gt;
&lt;div class=&quot;div-green&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Tip:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;When implementing the State Pattern, ensure that each state is responsible for handling only the behavior relevant to that state. This separation of concerns makes your code easier to manage, extend, and debug. Avoid embedding logic for multiple states within a single class, as it defeats the purpose of using this pattern.&lt;/span&gt; &lt;/div&gt;
&lt;div class=&quot;div-red&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Caution:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;Be cautious when transitioning between states to avoid unexpected behavior. If a state change is not handled correctly, the program may enter an undefined or incorrect state, leading to inconsistent results. Always validate transitions and ensure that states are switched appropriately based on the application’s logic.&lt;/span&gt; &lt;/div&gt;
&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;The State Pattern is particularly useful when an object’s behavior changes frequently based on its state. However, if the number of states grows significantly, consider using a state machine framework to prevent excessive complexity and redundant code.&lt;/span&gt; &lt;/div&gt;
&lt;h2 id=&quot;-conclusion&quot;&gt;✅ Conclusion&lt;/h2&gt;
&lt;p&gt;The State Pattern is an effective solution for managing state-dependent behavior in software applications. By encapsulating state-specific logic within individual classes, it promotes cleaner, modular, and maintainable code.&lt;/p&gt;
&lt;p&gt;The Calculator example demonstrates how this pattern allows dynamic state transitions, ensuring the application behaves correctly as its state evolves. This approach is especially useful in scenarios requiring flexibility and extensibility, such as games, workflows, or applications with complex user interactions.&lt;/p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/stevsharp/StatePattern&quot;&gt;State Pattern Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://refactoring.guru/design-patterns/state&quot;&gt;State Pattern - Refactoring Guru&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dotnettutorials.net/lesson/state-design-pattern/&quot;&gt;State Design Pattern in C#&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/742/the-state-pattern</guid>
<pubDate>Fri, 17 Jan 2025 09:32:25 +0000</pubDate>
</item>
</channel>
</rss>