refactor: break circular dependency with DamageDealer and Healer interfaces
- Create src/magical-objects/magical-object-types.ts with DamageDealer and Healer interfaces - MagicalWeapon implements DamageDealer - HealingObject implements Healer - Character depends on interfaces instead of concrete classes
This commit is contained in:
parent
f29e3c456f
commit
0805623b68
@ -74,15 +74,15 @@ After a user-story-conversation produces an Allium spec and fast-check propertie
|
||||
|
||||
For each rule/invariant from the spec, decide whether to write a **property-based test** or an **example-based test**. Discuss with the user:
|
||||
|
||||
| Signal | Choose | Rationale |
|
||||
| --------------------------------------------------- | --------------- | -------------------------------------------- |
|
||||
| `fc.property` would be trivially short (< 5 lines) | Example-based | Property overhead not worth it |
|
||||
| Invariant is a simple arithmetic relationship | Example-based | One or two examples cover all cases |
|
||||
| State transition has a small, finite input space | Example-based | Exhaustive examples are feasible |
|
||||
| Invariant involves collections, sequences, or math | Property-based | Need random inputs to find edge cases |
|
||||
| Rule has complex guards (requires + ensures chains) | Property-based | Random inputs surface hidden preconditions |
|
||||
| User says "just show it works" | Example-based | Confidence test, not a robustness guarantee |
|
||||
| User says "prove it always holds" | Property-based | That's what properties are for |
|
||||
| Signal | Choose | Rationale |
|
||||
| --------------------------------------------------- | -------------- | ------------------------------------------- |
|
||||
| `fc.property` would be trivially short (< 5 lines) | Example-based | Property overhead not worth it |
|
||||
| Invariant is a simple arithmetic relationship | Example-based | One or two examples cover all cases |
|
||||
| State transition has a small, finite input space | Example-based | Exhaustive examples are feasible |
|
||||
| Invariant involves collections, sequences, or math | Property-based | Need random inputs to find edge cases |
|
||||
| Rule has complex guards (requires + ensures chains) | Property-based | Random inputs surface hidden preconditions |
|
||||
| User says "just show it works" | Example-based | Confidence test, not a robustness guarantee |
|
||||
| User says "prove it always holds" | Property-based | That's what properties are for |
|
||||
|
||||
**Default:** start with example-based tests. Escalate to property-based only when the user or the spec demands broader coverage. This keeps the yak list smaller and faster to execute.
|
||||
|
||||
@ -167,13 +167,13 @@ Clear description of the change.
|
||||
|
||||
## Integration with Other Skills
|
||||
|
||||
| Skill | When to run problem-breakdown after |
|
||||
| ---------------------- | ------------------------------------------------ |
|
||||
| user-story-conversation| After Allium spec + properties are produced |
|
||||
| distill | After spec extraction, before test generation |
|
||||
| tend | After spec changes, to plan implementation updates |
|
||||
| propagate | When propagate produces obligations, to break them into file-level steps |
|
||||
| weed | After divergence is found, to plan alignment fixes |
|
||||
| Skill | When to run problem-breakdown after |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
| user-story-conversation | After Allium spec + properties are produced |
|
||||
| distill | After spec extraction, before test generation |
|
||||
| tend | After spec changes, to plan implementation updates |
|
||||
| propagate | When propagate produces obligations, to break them into file-level steps |
|
||||
| weed | After divergence is found, to plan alignment fixes |
|
||||
|
||||
## Example: Full Breakdown
|
||||
|
||||
|
||||
@ -10,8 +10,8 @@ import type { Status } from './Status.ts';
|
||||
import { StatusAlive, StatusDead } from './Status.ts';
|
||||
import type { CharacterState } from './CharacterState.ts';
|
||||
import type { Faction } from './Faction.ts';
|
||||
import type { MagicalWeapon } from './MagicalWeapon.ts';
|
||||
import type { HealingObject } from './HealingObject.ts';
|
||||
import type { DamageDealer } from './magical-objects/magical-object-types.ts';
|
||||
import type { Healer } from './magical-objects/magical-object-types.ts';
|
||||
|
||||
export interface CharacterCtor {
|
||||
name: string;
|
||||
@ -233,10 +233,7 @@ export class Character {
|
||||
* Dead characters cannot use weapons. Only the owner can use a weapon.
|
||||
* Returns updated weapon and target.
|
||||
*/
|
||||
useWeapon(
|
||||
weapon: MagicalWeapon,
|
||||
target: Character,
|
||||
): { weapon: MagicalWeapon; target: Character } {
|
||||
useWeapon(weapon: DamageDealer, target: Character): { weapon: DamageDealer; target: Character } {
|
||||
// Dead characters cannot use weapons
|
||||
if (this.status.kind === 'dead') return { weapon, target };
|
||||
// Only the owner can use the weapon
|
||||
@ -249,10 +246,7 @@ export class Character {
|
||||
* Dead characters cannot use healing objects.
|
||||
* Returns updated object and character.
|
||||
*/
|
||||
useHealingObject(
|
||||
object: HealingObject,
|
||||
amount: number,
|
||||
): { object: HealingObject; character: Character } {
|
||||
useHealingObject(object: Healer, amount: number): { object: Healer; character: Character } {
|
||||
// Dead characters cannot use healing objects
|
||||
if (this.status.kind === 'dead') return { object, character: this };
|
||||
return object.heal(this, amount);
|
||||
|
||||
@ -9,8 +9,9 @@
|
||||
import { Character } from './Character.ts';
|
||||
import { Level } from './Level.ts';
|
||||
import { MagicalObject } from './MagicalObject.ts';
|
||||
import type { Healer } from './magical-objects/magical-object-types.ts';
|
||||
|
||||
export class HealingObject extends MagicalObject {
|
||||
export class HealingObject extends MagicalObject implements Healer {
|
||||
private constructor(
|
||||
health: number,
|
||||
maxHealth: number,
|
||||
|
||||
@ -7,8 +7,9 @@
|
||||
*/
|
||||
import { Character } from './Character.ts';
|
||||
import { MagicalObject } from './MagicalObject.ts';
|
||||
import type { DamageDealer } from './magical-objects/magical-object-types.ts';
|
||||
|
||||
export class MagicalWeapon extends MagicalObject {
|
||||
export class MagicalWeapon extends MagicalObject implements DamageDealer {
|
||||
readonly #damage: number;
|
||||
readonly #owner: Character;
|
||||
|
||||
|
||||
24
src/magical-objects/magical-object-types.ts
Normal file
24
src/magical-objects/magical-object-types.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Magical object type interfaces — break the circular dependency between
|
||||
* Character.ts and the magical object implementations.
|
||||
*
|
||||
* These interfaces describe magical objects from Character's point of view,
|
||||
* so Character can depend on abstractions rather than concrete classes.
|
||||
*/
|
||||
import type { Character } from '../Character.ts';
|
||||
import type { MagicalObjectStatus } from '../MagicalObject.ts';
|
||||
|
||||
/** A magical object that deals damage — from Character's point of view */
|
||||
export interface DamageDealer {
|
||||
readonly owner: Character;
|
||||
readonly health: number;
|
||||
readonly status: MagicalObjectStatus;
|
||||
use(target: Character): { weapon: DamageDealer; target: Character };
|
||||
}
|
||||
|
||||
/** A magical object that heals — from Character's point of view */
|
||||
export interface Healer {
|
||||
readonly health: number;
|
||||
readonly status: MagicalObjectStatus;
|
||||
heal(character: Character, amount: number): { object: Healer; character: Character };
|
||||
}
|
||||
13112
transcripts/break-down-horizontal-refactoring-into-yaks.html
Normal file
13112
transcripts/break-down-horizontal-refactoring-into-yaks.html
Normal file
File diff suppressed because one or more lines are too long
13112
transcripts/create-task-breakdown-with-yaks-skill.html
Normal file
13112
transcripts/create-task-breakdown-with-yaks-skill.html
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user