From grimoire
Assigns object creation responsibility to the class with the strongest relationship (aggregation, containment, close use, or initialization data) to reduce coupling.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:apply-creator-patternThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Assign the responsibility for creating an instance of class A to the class B that aggregates, contains, closely uses, or has the initialization data for A.
Assign the responsibility for creating an instance of class A to the class B that aggregates, contains, closely uses, or has the initialization data for A.
Adopted by: GRASP (Larman, 2004); the pattern is the principled basis behind factory methods in the Gang of Four "Design Patterns" and behind dependency injection container wiring in Spring, .NET DI, and Guice. Any enterprise OOP framework with a dependency injection model is implementing Creator systematically.
Impact: Arbitrary object creation — constructors called wherever they are needed — scatters instantiation logic across the codebase. When the constructor signature changes (new parameter, different type), every call site must be updated. Creator localizes instantiation to the class with the strongest relationship to the created object, reducing the number of classes that need updating when construction changes.
Why best: Creation is a form of coupling: every class that calls new X() is coupled to X's constructor. Creator minimizes this coupling by assigning creation to the class that is already coupled to X for other reasons. If Order already aggregates LineItem objects, it is already coupled to LineItem — Order creating LineItem adds no new coupling. A ReportGenerator creating LineItem would add coupling with no justification.
Sources: Larman, "Applying UML and Patterns" (Prentice Hall, 2004); Gamma et al., "Design Patterns" (Addison-Wesley, 1994); Seemann, "Dependency Injection Principles, Practices, and Patterns" (Manning, 2019)
Assign B the responsibility to create A if one or more of the following is true (more = stronger candidate):
| Criterion | Example |
|---|---|
| B aggregates A | Order aggregates LineItem → Order creates LineItem |
| B contains A (composition) | Document contains Paragraph → Document creates Paragraph |
| B closely uses A | OrderProcessor uses Receipt only → OrderProcessor creates Receipt |
| B has initialization data for A | OrderController has all data to create Order → OrderController creates Order |
If multiple classes meet criteria, choose the one with the most criteria.
# Bad — ReportGenerator creates LineItem with no relationship to it
class ReportGenerator:
def summarize(self, order_data):
items = [LineItem(d["name"], d["price"], d["qty"]) for d in order_data]
# ReportGenerator has no other reason to know about LineItem
# Good — Order aggregates LineItems; Order creates them
class Order:
def add_item(self, name, price, qty):
self.line_items.append(LineItem(name, price, qty))
# Order already owns LineItems; creation adds no new coupling
When creating an object requires non-trivial logic (validation, lookups, conditional construction), encapsulate creation in a factory method on the Creator class.
class Order:
@classmethod
def from_cart(cls, cart, customer):
order = cls(customer_id=customer.id)
for cart_item in cart.items:
if cart_item.product.in_stock: # non-trivial: stock check
order.add_item(cart_item.product, cart_item.quantity)
order.apply_loyalty_discount(customer) # non-trivial: discount
return order
When the same object is constructed in multiple places with the same or similar parameters, it belongs in a single factory method or factory class owned by the Creator.
# Bad — Address constructed in 3 places
class OrderController:
def create(self, data):
addr = Address(data["street"], data["city"], data["zip"])
class CustomerController:
def update(self, data):
addr = Address(data["street"], data["city"], data["zip"])
class ShippingService:
def ship(self, data):
addr = Address(data["street"], data["city"], data["zip"])
# Good — centralized factory; only one place to change when Address evolves
class Address:
@classmethod
def from_form_data(cls, data):
return cls(data["street"], data["city"], data["zip"])
A class that creates an object and also uses it heavily is both a Creator and a User — two responsibilities. When creation logic becomes complex, extract a Factory or Builder.
# Bad — OrderService both creates and processes orders (creation logic is complex)
class OrderService:
def process(self, request):
order = Order(
user=self.user_repo.find(request.user_id),
items=[self.product_repo.find(pid) for pid in request.product_ids],
coupon=self.coupon_repo.find(request.coupon_code) if request.coupon_code else None,
)
return self._process_order(order)
# Good — factory handles complex creation; service handles processing
class OrderFactory:
def from_request(self, request) -> Order: ...
class OrderService:
def __init__(self, factory: OrderFactory): ...
def process(self, request):
order = self.factory.from_request(request)
return self._process_order(order)
When the created class has external dependencies (database, HTTP), creating it directly inside the Creator introduces coupling to infrastructure. Use dependency injection: the Creator receives the factory or the created object instead of constructing it.
# Bad — OrderProcessor constructs PaymentGateway; test requires real gateway
class OrderProcessor:
def process(self, order):
gateway = StripeGateway(api_key=os.environ["STRIPE_KEY"])
return gateway.charge(order.total())
# Good — gateway injected; test injects a fake
class OrderProcessor:
def __init__(self, gateway: PaymentGateway):
self.gateway = gateway
def process(self, order):
return self.gateway.charge(order.total())
Creating objects in classes with no relationship to them. If class B has no aggregation, containment, usage, or data relationship with A, B should not create A. Find or create the class that does have this relationship.
Over-centralizing in a single factory. A GlobalFactory that creates all objects in the system is not applying Creator — it is creating a coupling hub. Each type should be created by its most closely related class.
Confusing Creator with Dependency Injection. Creator determines which class is responsible for creation. DI determines how dependencies are supplied. Both can apply to the same object: Creator says "Order creates LineItem"; DI says "OrderService receives OrderFactory via injection."
Money, Address, DateRange) are typically created at the call site with their initialization data; applying Creator here adds indirection without benefitnpx claudepluginhub jeffreytse/grimoire --plugin grimoireApplies the Factory Method pattern to delegate object creation to subclasses or configuration, reducing coupling and enabling extensibility.
Provides TypeScript implementations of the Factory Method pattern, including class-based, function-based, and generic registry approaches. Useful for decoupling object creation from client code.
Generates DDD-compliant factories for PHP 8.4 domain objects, encapsulating complex creation logic with input validation and unit tests. Ideal for intricate entity instantiation, aggregates, and reconstruction from persistence.