Token导航 LogoToken导航TokenDH.com
研究检索external-serviceclawhub未标认证来源可访问clear审计通过

bookforge-type-code-refactoring-selectorbookforge 类型代码重构选择器

Agent Skill

bookforge-type-code-refactoring-selector 用于查找、检索和筛选相关信息,适合在 OpenClaw 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

3,608

周安装

155

GitHub Stars

公开资料未说明

下载量

1,265
OpenClaw

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

MIT-0

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:bookforge-type-code-refactoring-selector(bookforge 类型代码重构选择器)
来源仓库:https://github.com/quochungto/bookforge-type-code-refactoring-selector
安装命令:
openclaw skills install bookforge-type-code-refactoring-selector
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install bookforge-type-code-refactoring-selector

简介

识别字符串标记的类型代码并推荐重构路径。

  • 转换为枚举、常量或标记对象提升类型安全。
  • 减少魔法字符串带来的维护成本。bookforge-type-code-refactoring-selector 属于研究检索类 Skill,可作为该场景下的辅助能力补充。
  • 作为 OpenClaw 重构辅助工具提供代码改进建议。
  • 需评估现有调用点的兼容性影响范围。

SKILL.md

name
type-code-refactoring-selector
description
|
version
1.0.0
homepage
https://github.com/bookforge-ai/bookforge-skills/tree/main/books/refactoring/skills/type-code-refactoring-selector
metadata
{"openclaw":{"emoji":"📚","homepage":"https://github.com/bookforge-ai/bookforge-skills"}}
status
draft
depends-on
source-books
title
Refactoring: Improving the Design of Existing Code
authors
["Martin Fowler", "Kent Beck"]
chapters
[3, 8]
tags
[refactoring, code-quality, type-codes, polymorphism]
execution
tier
2
mode
hybrid
inputs
description
Source code containing the type code — the class that holds the integer/enum constant and the methods that switch on it
description
Code snippet or class description if no live codebase is accessible
tools-required
[Read, Grep, Write]
tools-optional
[Bash]
mcps-required
[]
environment
Run inside a project directory. Reading source files and grepping for switch/case patterns on the type code are the primary analysis methods.
discovery
goal
Correctly route a type code to one of three replacement paths; execute the full mechanics for the chosen path; eliminate the original conditional branching with polymorphism
tasks
audience
roles
["software-developer", "senior-developer", "tech-lead"]
experience
intermediate — assumes working knowledge of object-oriented design and subclassing
triggers
not_for

Type Code Refactoring Selector

When to Use

You have a class that stores a type code: an integer constant, an enum, or a string tag whose value identifies which variant of the object this is. The code switches on this value in multiple places — different methods, different callers — and adding a new variant means hunting down every switch and adding a case.

This skill applies when:

  • A switch (_type) or if (type == ENGINEER) pattern recurs across the codebase, driven by the same type code field
  • The code-smell-diagnosis skill has named Switch Statements or Primitive Obsession and the underlying cause is a type code
  • A new business variant (new employee role, new payment method, new order state) keeps requiring edits in multiple places
  • The type code is passed as a raw int — the compiler cannot enforce valid values, and callers can pass arbitrary numbers

The core insight from Fowler: Polymorphism eliminates the need to know which variant you have. Instead of asking "what type are you?" and branching, you call a method and each variant answers differently. But to reach polymorphism, you first need the right structural foundation — and which foundation you build depends on two criteria: does the type code affect behavior, and can the class be subclassed?

Upstream dependency: code-smell-diagnosis identifies the smell. This skill executes the remedy once Switch Statements or Primitive Obsession (type code variant) has been diagnosed.

Downstream: After this skill, use conditional-simplification-strategy if any complex conditionals remain that are not type-code-driven.


Context and Input Gathering

Required Input (ask if missing)

  • The class holding the type code. File path or class name. Why: the mechanics work on the class that owns the type code field and its accessors. Everything else is found from there.
  • The type code field and its values. Name of the field; the named constants and their meanings (e.g., ENGINEER = 0, SALESMAN = 1, MANAGER = 2). Why: each value becomes either a subclass or a state object; knowing the full value set before starting prevents incomplete refactoring.

Observable Context (gather before asking)

Grep for the type code to map the full scope before choosing a path:

Search targets:
  - The type code field name across all files → how many switch sites are there?
  - switch (_type) or if (type == X) patterns → do they affect behavior or just read the value?
  - The class's extends clause → does it already have a superclass?
  - Setter methods on the type code → does the type change after object creation?
  - Subclasses of the class → are there existing subclasses for another reason?

Default Assumptions

  • If the type code is only read (no switch statements execute different logic per value): treat as non-behavior-affecting → Path A
  • If it is unclear whether the type changes at runtime: check for setter methods on the type field. Presence of a setter = mutable type = cannot use subclasses → Path C
  • If the class already extends another class: cannot add a second superclass in single-inheritance languages → Path C

The Three-Way Decision Tree

Does the type code drive different behavior?
(switch statements / if-else chains execute different code per value)
│
├── NO ──→ PATH A: Replace Type Code with Class
│          (type code is pure data — used for identity/classification only,
│           no conditional branching on its value)
│
└── YES
    │
    Can the class be subclassed?
    (No existing inheritance blocking it AND type value does not change
     after the object is created — no setter on the type field)
    │
    ├── YES ──→ PATH B: Replace Type Code with Subclasses
    │           (simplest path — the class itself grows subclasses,
    │            one per type code value)
    │
    └── NO ──→ PATH C: Replace Type Code with State/Strategy
                (class already subclassed for another reason, OR
                 type value changes at runtime — use a state object
                 that holds the variant behavior instead)

State vs. Strategy sub-decision (within Path C):

  • Simplifying a single algorithm with Replace Conditional with Polymorphism → name it Strategy (behavior is the unit being varied)
  • Object feels like it transitions between states at runtime (e.g., an order moves from PENDING to SHIPPED to DELIVERED) → name it State (object identity is the unit being varied)
  • Mechanically identical — the naming reflects intent, not structure

Process

Step 1: Identify and Scope the Type Code

ACTION: Read the class holding the type code. Grep for the type code constants and field name across the entire codebase.

WHY: A type code refactoring that only touches the host class and misses switch statements in callers produces an inconsistent codebase — old integer-based calls mixed with new class-based ones. The full scope must be known before any transformation begins.

Identify:

  1. The type code field (private int _type, private String status, etc.)
  2. All named constant values and their meanings
  3. Every location in the codebase that switches on or compares against this field
  4. Whether those locations execute different behavior per value (not just format or display the value)
  5. Whether the field has a setter (mutable = cannot subclass directly)
  6. Whether the host class already extends another class

Output of this step: A table like:

Type code field: Employee._type
Values: ENGINEER(0), SALESMAN(1), MANAGER(2)
Switch sites:
  - Employee.payAmount() — behavioral (different pay calculation per type)
  - ReportGenerator.formatTitle() — behavioral (different title per type)
  - EmployeeDAO.save() — non-behavioral (writes type code as integer to DB)
Type mutable: YES (setType() method exists) → cannot use subclasses
Host class inherits: NO
Decision: PATH C — Replace Type Code with State/Strategy

Step 2: Route to the Correct Path

ACTION: Apply the decision tree using the observations from Step 1. Select Path A, B, or C.

WHY: Each path produces a fundamentally different structure. Applying Path B (subclasses) when the type changes at runtime will break the object model — an object cannot change its class. Applying Path A when the type code drives behavior leaves the switch statements in place and produces no improvement. The routing decision is the highest-leverage moment in this skill.

Decision criteria (in order):

  1. Are there switch statements or if-else chains that execute different code depending on the type code's value?

- No → Path A - Yes → continue to criterion 2

  1. Does the type code field have a setter, or does the host class already extend another class?

- No setter AND no existing superclass → Path B - Setter exists OR existing superclass → Path C

Exception checks (apply before executing the chosen path):

  • Polymorphism overkill: If the switch affects only a single method and the variants are not expected to grow — use Replace Parameter with Explicit Methods instead of the full type code replacement. Signal: the type code appears in only one switch in one method, and the method's callers already know which variant they want.
  • Null case: If one of the cases in the switch is null — apply Introduce Null Object for that case before or alongside the main path.

Step 3A: Path A — Replace Type Code with Class

Precondition: Type code is pure data — no switch statements execute different behavior per value.

WHY this path: The compiler sees integers, not type names. Any integer can be passed, including invalid ones. Replacing the integer with a class gives the compiler the ability to enforce valid values at call sites. It also creates a home for behavior that belongs to the type (Move Method opportunities).

Mechanics:

  1. Create the type code class.

- Name it after the concept the type code represents (e.g., BloodGroup, EmployeeType) - Give it a private integer field _code that stores the underlying value - Add a private constructor taking the integer code - Add static final instances for each value: public static final BloodGroup O = new BloodGroup(0); - Add a private static array _values to map integers to instances - Add a public static factory method code(int arg) that returns the correct instance from _values - Add a public getCode() method that returns the integer (needed during the transition)

  1. Modify the host class to use the new type.

- Change the type of the field from int to the new class - Keep old integer-based constants pointing at NewClass.INSTANCE.getCode() so existing callers still compile - Update the constructor and any setter to use NewClass.code(intArg) to convert incoming integers - Compile and test — this is a safe checkpoint

  1. Migrate callers one by one.

- For each caller using the integer constants: change to use the new class's static instances (BloodGroup.O instead of Person.O) - For each caller using the integer getter: change to use the new class-returning getter - Rename the old integer getter before adding the new class getter to make the transition visible: getBloodGroupCode() (old) → getBloodGroup() (new, returns BloodGroup) - Compile and test after each caller is updated

  1. Remove the old integer interface.

- Once all callers use the new class, remove the integer-based constants, the integer-based getter, and the integer-based constructor - Privatize getCode() on the type class — it is now an implementation detail - Compile and test

Alert: Even if the type code does not cause different behavior in switch statements, check whether any behavior would be better placed on the new type code class. Apply Move Method for any method that primarily operates on the type value.


Step 3B: Path B — Replace Type Code with Subclasses

Precondition: Type code drives behavior (switch statements exist) AND the type value is immutable after object creation AND the host class has no existing superclass blocking subclassing.

WHY this path: The simplest path to polymorphism. Each type code value becomes a subclass of the host class. The subclasses override the type code getter to return their specific value (a temporary measure), then the switch statements are replaced with polymorphic method dispatch. Knowledge of which variant you have moves from callers into the class itself.

Mechanics:

  1. Self-encapsulate the type code.

- Add a getter method for the type code field if one does not exist: int getType() { return _type; } - Replace all direct field access in the host class with calls to the getter - Why: subclasses will override the getter; direct field access would bypass the override

  1. Replace the constructor with a factory method (if the type code is passed to the constructor).

- Create a static factory: static Employee create(int type) { return new Employee(type); } - Make the constructor private - Update all callers to use the factory — compile and test - Why: once there are subclasses, the factory will instantiate the correct subclass. The constructor cannot be made to return a subclass instance.

  1. Create one subclass per type code value.

- For each value (ENGINEER, SALESMAN, MANAGER): create a subclass that overrides the type code getter to return the hard-coded value

   class Engineer extends Employee {
     int getType() { return Employee.ENGINEER; }
   }

- Update the factory method to return the correct subclass per value:

   static Employee create(int type) {
     if (type == ENGINEER) return new Engineer();
     else if (type == SALESMAN) return new Salesman();
     // etc.
   }

- Compile and test after adding each subclass

  1. Remove the type code field from the superclass.

- Once all subclasses override the getter, the field in the superclass is unused - Remove the field; declare getType() abstract in the superclass - Compile and test

  1. Apply Replace Conditional with Polymorphism (Step 4 below) on all switch statements that remain.
  1. Push down type-specific features.

- Use Push Down Method and Push Down Field on any methods or fields that are only relevant to certain subclasses - These are now clearly expressed by being in the subclass rather than guarded by a type code check


Step 3C: Path C — Replace Type Code with State/Strategy

Precondition: Type code drives behavior (switch statements exist) AND either (a) the type value changes at runtime (mutable type), OR (b) the host class already has a superclass.

WHY this path: Path B requires the host class to be extended. When that is not possible — the host already has a superclass (single inheritance), or the type changes during the object's lifetime — the polymorphic behavior must live in a separate object. The host class delegates to a state/strategy object, which can be swapped at runtime without changing the host object's class.

Mechanics:

  1. Self-encapsulate the type code.

- Add getter and setter for the type code field if not present - Replace all direct field access in the host class with calls to the getter/setter - Why: the getter and setter will be redirected to delegate to the state object in step 4

  1. Create the state/strategy abstract class.

- Name it after the purpose of the type code (e.g., EmployeeType for an employee type code) - Declare an abstract method to return the type code: abstract int getTypeCode(); - Why: the host class's existing getter will delegate to this method; all callers of the host's getter continue to work unchanged

  1. Create one concrete subclass per type code value.

- Add all subclasses at once (easier than one at a time for this path) - Each subclass returns its specific type code from getTypeCode()

   class Engineer extends EmployeeType {
     int getTypeCode() { return Employee.ENGINEER; }
   }

- Compile

  1. Connect the host class to the state/strategy object.

- Add a private field of the state class type: private EmployeeType _type; - Redirect the host class's type code getter to delegate: int getType() { return _type.getTypeCode(); } - Redirect the host class's type code setter to instantiate the correct state subclass:

   void setType(int arg) {
     switch (arg) {
       case ENGINEER: _type = new Engineer(); break;
       case SALESMAN: _type = new Salesman(); break;
       // etc.
     }
   }

- Update the constructor to call the setter (not directly assign) - Compile and test - Note: this creates one switch statement in the setter. It is acceptable — it is isolated to object creation/transition and will be the only switch remaining once Replace Conditional with Polymorphism eliminates all the behavioral switches

  1. Move type code constants to the state class.

- Copy the constant definitions into EmployeeType; add a factory method newType(int code) with the switch - Update the host class's setter to call EmployeeType.newType(arg) instead of the switch - Remove the constant definitions from the host class; update any remaining references to use EmployeeType.ENGINEER etc. - Compile and test

  1. Apply Replace Conditional with Polymorphism (Step 4 below) on all behavioral switch statements.

Step 4: Replace Conditional with Polymorphism

ACTION: For each switch statement (or if-else chain) that branches on the type code, replace it with a polymorphic method call.

WHY: This is the payoff step. After Paths B and C, the type code has an inheritance structure. Replace Conditional with Polymorphism moves each branch of each switch into the appropriate subclass. The switch disappears; callers simply invoke the method and get the right behavior for their variant automatically. Adding a new variant now means adding one subclass — no switch hunting.

Mechanics (apply per switch statement):

  1. Extract Method on the switch statement if it is embedded in a longer method. Give it a name that describes what it computes.
  2. Move Method to the class that holds the type code (the host class in Path B, or the state/strategy abstract class in Path C).
  3. For each case in the switch:

- Create an overriding method in the appropriate subclass that contains that case's body - The superclass version becomes abstract (or raises an error if there is a default case that should never be reached)

  1. Delete the switch statement from the moved method; make it abstract.
  2. Compile and test after each case is moved.

After all switches are replaced:

  • The type code field exists only in the getter (Path B: abstract; Path C: delegating to state object)
  • No caller needs to ask what type an object is — they just call the method
  • Adding a new variant is a single-class addition

Step 5: Apply Exceptions and Reverse Path

Replace Parameter with Explicit Methods (polymorphism overkill):

Apply instead of the full type code replacement when ALL of these are true:

  • The switch appears in only one method
  • The method takes the type code as a parameter (not a stored field)
  • The variants are stable — new cases are not expected

Mechanics: Create a separate named method for each case. Instead of one setHeight(int metricType, double amount) with a switch on metricType, create setHeightInMeters(double amount) and setHeightInFeet(double amount). Callers use the explicit method name instead of passing a type code constant.

Introduce Null Object:

Apply when one of the switch cases handles null:

  • Create a null-object subclass that returns neutral/safe values for every method (zero, empty string, no-op, etc.)
  • Replace all null checks in callers with polymorphic calls — the null object responds correctly without special-casing
  • The null check disappears from the switch

Replace Subclass with Fields (reverse path):

Apply when you have subclasses that vary only in constant return values — no real behavior difference, just returning different hard-coded constants.

Signal: subclasses that look like:

class Male extends Person { boolean isMale() { return true; } char getCode() { return 'M'; } }
class Female extends Person { boolean isMale() { return false; } char getCode() { return 'F'; } }

Mechanics:

  1. Apply Replace Constructor with Factory Method on the subclasses — create factory methods on the superclass (createMale(), createFemale())
  2. Update all callers to use the factory methods; remove direct references to the subclass names
  3. For each constant method on the subclasses: declare a field on the superclass; add a protected superclass constructor that initializes those fields; update subclass constructors to call super(true, 'M') etc.
  4. Implement each constant method on the superclass to return the field; remove the override from the subclass
  5. Compile and test after each subclass method is removed
  6. When a subclass has no remaining methods: use Inline Method to inline its constructor into the superclass factory method; delete the subclass
  7. Repeat until all constant-only subclasses are gone

Decision Summary (Quick Reference)

SituationPath
Type code is pure data — no switches on valueA: Replace Type Code with Class
Type code drives behavior + class can be subclassed + type is immutableB: Replace Type Code with Subclasses
Type code drives behavior + (class already subclassed OR type changes at runtime)C: Replace Type Code with State/Strategy
Focusing on a single algorithmName the object Strategy
Object transitions between states at runtimeName the object State
Switch affects only one method, variants stableReplace Parameter with Explicit Methods
One switch case is nullIntroduce Null Object
Subclasses vary only in constant return valuesReplace Subclass with Fields (reverse)

Examples

Example 1: Path A — Blood Group (Pure Data)

Situation: Person class stores blood group as int _bloodGroup with constants O=0, A=1, B=2, AB=3. No method switches on blood group value — it is stored and retrieved for display. The type code is pure data.

Routing: No behavior-affecting switch → Path A.

Outcome: Create BloodGroup class with static instances O, A, B, AB. Person stores private BloodGroup _bloodGroup. All callers that used Person.O now use BloodGroup.O. The compiler now rejects person.setBloodGroup(99) — invalid blood groups become compile errors, not runtime surprises.


Example 2: Path B — Employee Type (Immutable, No Inheritance Conflict)

Situation: Employee stores int _type with ENGINEER=0, SALESMAN=1, MANAGER=2. Method payAmount() switches on _type to calculate different pay. The type is set once at construction. Employee has no existing superclass.

Routing: Behavior-affecting switch + no setter + no existing superclass → Path B.

Outcome: Engineer, Salesman, Manager subclasses of Employee. payAmount() becomes abstract on Employee; each subclass implements its own pay calculation. Adding a new employee type (Contractor) means adding one class — no switch hunting.


Example 3: Path C — Employee Type (Mutable — Can Change Role)

Situation: Same Employee as above, but employees can be promoted (a manager can become an engineer). setType(int arg) exists. Subclassing is impossible because the type changes at runtime.

Routing: Behavior-affecting switch + setter exists (type mutable) → Path C.

Outcome: EmployeeType abstract class with Engineer, Salesman, Manager subclasses. Employee holds private EmployeeType _type. payAmount() moves to EmployeeType and is overridden in each subclass. Calling employee.setType(Employee.ENGINEER) swaps in a new Engineer state object. The object model is valid — Employee does not change class, its delegate does.


Example 4: Polymorphism Overkill — Replace Parameter with Explicit Methods

Situation: A single method setValue(int metricType, double amount) switches on metricType (FEET=0, METERS=1). This switch appears only in this one method.

Routing: Single method affected, stable variants → Replace Parameter with Explicit Methods.

Outcome: setValueInFeet(double amount) and setValueInMeters(double amount). Callers are more readable; the compiler enforces the call rather than trusting the caller to pass a valid constant.


Key Principles

1. Route before executing. The most expensive mistake is applying the wrong path. Applying Path B when the type is mutable requires undoing the subclass structure and building a state object instead. Take 10 minutes to answer the two decision tree questions before writing any code.

2. Self-encapsulate first, always. Both Paths B and C begin with self-encapsulation of the type code field. Skipping this step and leaving direct field access in the host class means subclasses' getter overrides are bypassed. The refactoring silently fails.

3. One switch is acceptable after Path C. The setter in Path C has a switch that instantiates the correct state subclass. This is the only remaining switch and it is isolated to one place. Do not try to eliminate it with more indirection — it is the factory, and one factory switch is exactly right.

4. Migrate callers one at a time. In Path A especially, changing all callers at once is error-prone. Change one caller, compile, test, then the next. The old and new interfaces can coexist during the transition — that is the point of keeping getCode() available until all callers have migrated.

5. Replace Conditional with Polymorphism is the payoff. Paths B and C are scaffolding. The actual gain — no more switch hunting when adding variants — comes from completing Replace Conditional with Polymorphism. Do not stop at the structural step.


References

FileContentsWhen to read
references/refactoring-prescriptions.mdFull prescription tree per smell with all conditional branchesVerifying routing decisions
references/smell-catalog.mdSwitch Statements and Primitive Obsession detection criteriaConfirming the type code is the root cause

Skill relationships:

  • code-smell-diagnosis — upstream: identifies Switch Statements or Primitive Obsession that routes here
  • conditional-simplification-strategy — downstream: handles complex conditionals not driven by type codes
  • data-organization-refactoring — parallel: handles Primitive Obsession when the issue is data clusters, not type codes

License

This skill is licensed under CC-BY-SA-4.0. Source: BookForge — Refactoring: Improving the Design of Existing Code by Martin Fowler and Kent Beck.

Related BookForge Skills

Install related skills from ClawhHub:

  • clawhub install bookforge-code-smell-diagnosis
  • clawhub install bookforge-conditional-simplification-strategy
  • clawhub install bookforge-data-organization-refactoring

Or install the full book set from GitHub: bookforge-skills

适合场景

01

OpenClaw 用户查找和安装 Skill 时

02

用户想查找某类 Agent Skill 时

03

需要根据任务场景推荐可安装能力包时

04

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenClaw

88.93%
按下载量换算1,125

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

通过

权限和风险

external-service

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills