Refactor: Script to Object Model¶
Page Maps¶
graph LR
family["Python Programming"]
program["Python Object-Oriented Programming"]
section["Object Semantics Data Model"]
page["Refactor: Script to Object Model"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
script["inspect the script's existing meanings"] --> values["extract the stable values first"]
values --> owners["delay identity until continuity is real"]
owners --> hazards["repair shared-state and copy hazards"]
hazards --> review["review the resulting object contracts"]
This refactor matters because the first mistake many learners make is turning a working script into classes before they know what those classes are supposed to mean. The result often looks more object-oriented while becoming less trustworthy:
- bags of fields with no contract
- identity where value semantics were needed
- mutation where construction should have settled the truth
- aliasing and copy bugs that the original script hid
The goal of this refactor is not "object-oriented style." The goal is to turn raw script data into types whose identity, equality, mutation, and representation rules are understandable.
Start by naming the meanings already present¶
A script often contains several kinds of things mixed together:
- raw input records
- stable values
- evolving workflow state
- temporary derived data
- convenience caches or formatting helpers
Before creating any class, ask:
- which pieces are really values?
- which pieces need continuity over time?
- which pieces are just temporary helper structures?
- which pieces should stay plain data until later pressure proves otherwise?
If you cannot answer those questions, the refactor is not ready yet.
A safer refactor order¶
Use this order on purpose:
- identify the stable values
- extract the smallest validated value types
- keep temporary derived data plain until a real contract appears
- introduce identity-bearing objects only where lifecycle or continuity is real
- repair shared mutable structures before they disappear inside prettier classes
- decide equality, hashing, and representation on purpose
This order keeps semantics in front of style.
Step 1: convert obvious values first¶
Strong early candidates for object form are small values with clear meaning, such as:
- metric names
- timestamps
- threshold definitions
- validated inputs that should compare by content
These types benefit from:
- explicit validation
- predictable equality
- clear representation
- safe sharing
They are usually much safer to introduce first than a large mutable coordinator.
Step 2: delay identity until continuity is real¶
Not every record should become an identity-bearing object.
Introduce identity only when the system genuinely needs to track:
- lifecycle
- continuity across time
- distinct instances even when fields match
- transitions that should not collapse into plain value equality
Premature entities make later decisions about equality, hashing, mutation, and review much harder.
Step 3: let construction settle required truth¶
Weak refactors often create objects half-empty and repair them later through ad hoc mutation. A stronger refactor asks:
- what must be true at birth?
- what should become a later transition instead?
- which constructor arguments are real requirements and which are placeholders for weak design?
Prefer constructors that establish:
- required fields
- validation
- clear starting state
That reduces the number of places where another maintainer must guess whether the object is already safe to use.
Step 4: repair shared-state hazards before they hide inside classes¶
Scripts often pass around:
- shared lists
- reused dictionaries
- caller-owned mutable structures
- nested mutable graphs that nobody clearly owns
When these become objects, decide:
- which values are safe to share because they are immutable?
- which mutable structures need one clear owner?
- where copying is honest and where redesign is safer?
This is where many "successful" refactors still fail, because the code now looks cleaner while the ownership bug survives untouched.
Step 5: define equality and representation deliberately¶
Once a value object exists, ask:
- what makes two instances equal?
- should it be hashable?
- what should the representation teach a reader?
Once an entity exists, ask:
- should equality stay by identity?
- should it stay out of hashed collections?
- what representation helps debugging without pretending it is a plain value?
These are design decisions, not cleanup steps.
Worked refactor route¶
Imagine a script that uses dictionaries for:
- metric samples
- threshold configuration
- alert rows
A safer refactor route could be:
- turn threshold configuration into a validated value type
- turn metric samples into a small value type with explicit equality and representation
- keep alert rows as temporary derived data unless lifecycle pressure proves otherwise
- identify the first object that truly owns mutation and make that ownership explicit
- remove shared mutable container reuse by copying, freezing, or encapsulating the mutable boundary
Notice what does not happen first:
- no giant manager class
- no framework-style hierarchy
- no entity identity everywhere
The refactor begins with meaning, not with class count.
Before-and-after review lens¶
| Question | Weak script-to-class refactor | Stronger Module 01 refactor |
|---|---|---|
| why does this class exist | because the script had a dictionary | because it protects a real contract |
| how is equality decided | whatever the default gives me | by the meaning of the object |
| who owns mutable state | unclear, often shared | explicit and reviewable |
| when is the object valid | after callers finish setting fields | at construction or through named transitions |
| what does the representation teach | incidental internal structure | the part of meaning a reader actually needs |
Common mistakes¶
- turning every dictionary into a class without deciding whether it is a value, entity, or temporary shape
- giving identity to objects that only need content semantics
- leaving required state unset until long after construction
- preserving shared mutable containers inside prettier-looking classes
- ignoring equality, hashing, and
repruntil the bugs appear later
Capstone transfer¶
Use the capstone's early value and rule types to ask:
- which parts became trustworthy because they were modeled as values first?
- which types would become less clear if they were given identity too early?
- where would a shared mutable script structure have created later lifecycle trouble?
This is the first refactor discipline of the course: class form should follow meaning.
Exit check¶
Leave this refactor page only when you can do all of these:
- explain how you would decide whether script data should become a value object, an entity, or stay plain data
- identify one aliasing hazard that a naive script-to-class refactor could preserve
- state one reason a value-first refactor is safer than building a large mutable manager class