Design Patterns and Strategies
Design patterns provide structured approaches to solving common formatting challenges in Python. They enable developers to create more flexible, maintainable, and scalable formatting solutions.
Core Concept
The Strategy Pattern allows dynamic selection of formatting algorithms at runtime.
from abc import ABC, abstractmethod
class FormattingStrategy(ABC):
@abstractmethod
def format(self, data):
pass
class JSONFormatter(FormattingStrategy):
def format(self, data):
import json
return json.dumps(data, indent=2)
class CSVFormatter(FormattingStrategy):
def format(self, data):
import csv
import io
output = io.StringIO()
writer = csv.writer(output)
writer.writerows(data)
return output.getvalue()
class Formatter:
def __init__(self, strategy):
self._strategy = strategy
def format(self, data):
return self._strategy.format(data)
Visualization of Strategy Pattern
classDiagram
class FormattingStrategy {
+format(data)
}
class JSONFormatter {
+format(data)
}
class CSVFormatter {
+format(data)
}
class Formatter {
-strategy
+format(data)
}
FormattingStrategy <|-- JSONFormatter
FormattingStrategy <|-- CSVFormatter
Formatter --> FormattingStrategy
Implementation Example
class FormatterDecorator:
def __init__(self, formatter):
self._formatter = formatter
def format(self, data):
return self._formatter.format(data)
class UppercaseDecorator(FormatterDecorator):
def format(self, data):
return super().format(data).upper()
class PaddingDecorator(FormatterDecorator):
def __init__(self, formatter, width=10):
super().__init__(formatter)
self._width = width
def format(self, data):
formatted = super().format(data)
return formatted.center(self._width)
Composition vs Inheritance Strategies
Approach |
Pros |
Cons |
Inheritance |
Simple implementation |
Less flexible |
Composition |
More flexible |
More complex |
Mixins |
Modular |
Can lead to complexity |
Fluent Interface Pattern
class FluentFormatter:
def __init__(self, data):
self._data = data
self._transformations = []
def uppercase(self):
self._transformations.append(str.upper)
return self
def truncate(self, length):
self._transformations.append(lambda x: x[:length])
return self
def format(self):
result = self._data
for transform in self._transformations:
result = transform(result)
return result
## Usage example
formatted_text = (FluentFormatter("hello world")
.uppercase()
.truncate(5)
.format()) ## Returns "HELLO"
class ConfigurableFormatter:
def __init__(self, config):
self._config = config
def format(self, data):
formatted = data
for rule in self._config:
formatted = rule(formatted)
return formatted
## Example configuration
def uppercase(x): return x.upper()
def add_prefix(prefix):
return lambda x: f"{prefix}{x}"
config = [
uppercase,
add_prefix("LabEx: ")
]
formatter = ConfigurableFormatter(config)
result = formatter.format("python programming")
Conclusion
Effective formatting design requires a thoughtful approach to selecting appropriate patterns and strategies. By understanding these techniques, developers can create more robust and adaptable formatting solutions.