Refactoring your code helps make it easier to manage and understand.
One common pattern that can make code complex is using 'type code'. This is when you have an enum
or other type indicator in a class and use switch
statements or if/else
chains to perform different actions based on that type.
The Problem: Type Code
Imagine a Document
class with a DocumentType
enum (pdf
, doc
, markdown
). Inside a method like write
, you might use a switch
statement to decide what to do based on document.type
. This works, but it means you need to add a new case
every time you add a new document type. These switch
statements can spread throughout your codebase, making updates tricky.
The Solution: Union Types (Sealed Classes in Dart)
Dart 3 introduces sealed classes, which are great for representing a fixed set of types. Instead of one Document
class with a type field, you can create a sealed DocumentSealed
class with subclasses like PdfDocument
, DocDocument
, and MarkdownDocument
. Each subclass extends the sealed class.
How it Works
- The original type field (
DocumentType type
) is gone from the main class concept. - Each specific type (like
Pdf
,Doc
) becomes its own class. - Methods like
write
are defined in the sealed base class, but each subclass provides its own specific implementation. - When you need to handle the document, you can use Dart's sealed class support for exhaustive checking, often with a
switch
expression.
Why This Is Better
- Cleaner Code: It removes scattered
switch
statements. - Better Organization: Logic for each type is contained within its own class.
- Easier to Add New Types: When you add a new document type, you create a new subclass. The compiler will help you find places where you need to handle the new type because
switch
expressions on sealed types are exhaustive. - More Readable: It clearly shows the different kinds of documents and their specific behaviors.
- Easier Testing: You can test the behavior of each specific document type in isolation.
When to Use This Pattern
Use this refactoring when a class needs to behave differently based on its state or type, and you find yourself using switch
statements on a type field frequently. For example, if certain operations are only possible for some document types (like you can't 'write' to a PDF in your system).