Token导航 LogoToken导航

Agent Skill · 研究检索

bookforge-conditional-simplification-strategy(Bookforge 条件简化策略)

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

bookforge-conditional-simplification-strategy 是研究检索类 Agent Skill,可用宿主为 OpenClaw,通过ClawHub获取,权限为调用外部服务;许可证为 MIT-0,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 23,228。

总安装 4,326周安装 175GitHub Stars 公开资料未说明下载 1,358核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT-0
版本
1.0.0

安装方式

通过对话安装

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

请帮我安装这个 Agent Skill:bookforge-conditional-simplification-strategy(Bookforge 条件简化策略)
来源仓库:https://github.com/quochungto/bookforge-conditional-simplification-strategy
安装命令:
openclaw skills install bookforge-conditional-simplification-strategy
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

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

ClawHubOpenClaw
openclaw skills install bookforge-conditional-simplification-strategy

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

针对复杂条件逻辑选择合适重构策略简化代码。提升分支可读性与维护性,降低认知负担。适用于存在深层嵌套 if-else 结构的函数或模块。

安装命令:openclaw skills install bookforge-conditional-simplification-strategy 需提供具体条件表达式示例以便匹配最佳方案。

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 OpenClaw;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 openclaw skills install bookforge-conditional-simplification-strategy;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要调用外部服务,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

bookforge-conditional-simplification-strategy 怎么安装?

在终端执行 openclaw skills install bookforge-conditional-simplification-strategy。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

bookforge-conditional-simplification-strategy 支持哪些 AI 工具?

公开资料标注支持 OpenClaw。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

bookforge-conditional-simplification-strategy 需要什么权限?

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

bookforge-conditional-simplification-strategy 是免费的吗?

来源标注的许可证是 MIT-0。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

bookforge-conditional-simplification-strategy 用的人多吗?

公开数据是累计安装 4,326 次,近一周安装 175 次,下载 1,358 次。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

bookforge-conditional-simplification-strategy 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 专注 OpenClaw 一种宿主,触发方式和使用习惯不用来回切换。
  • 许可证明确为 MIT-0,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 OpenClaw,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
bookforge-conditional-simplification-strategy

Bookforge 条件简化策略 · 本页

4,326—调用外部服务MIT-0通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

name
conditional-simplification-strategy
description
|
version
1.0.0
homepage
https://github.com/bookforge-ai/bookforge-skills/tree/main/books/refactoring/skills/conditional-simplification-strategy
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
[9]
tags
[refactoring, code-quality, conditionals, polymorphism]
execution
tier
2
mode
hybrid
inputs
description
Source code containing the conditional logic to refactor — the primary input
description
Code snippet or method body if no live codebase is accessible
tools-required
[Read, Grep, Write, Edit]
tools-optional
[Bash]
mcps-required
[]
environment
Run inside a project directory. Reading source files and grepping for related conditionals is the primary analysis method.
discovery
goal
Identify which of the 8 conditional refactorings applies to the target conditional; execute the selected refactoring correctly; leave the code with conditional logic that clearly separates the reason for branching from the details of each branch
tasks
audience
roles
["software-developer", "senior-developer", "tech-lead"]
experience
intermediate — assumes working knowledge of object-oriented programming and refactoring basics
triggers
not_for

Conditional Simplification Strategy

When to Use

You have conditional logic that is hard to read, hard to extend, or hiding its own intent. This includes:

  • A method-level conditional where the condition checks multiple things but the code only tells you *what* happens, not *why* the branching exists
  • Several separate conditions that all produce the same result, scattered as sequential checks
  • The same code fragment repeated in every branch of a conditional
  • A boolean variable toggled to control loop exit — the code is structuring state instead of expressing intent
  • Nested if-else blocks where finding the "normal" path requires reading through multiple levels of special cases
  • A switch (or long if-else-if chain) that dispatches behavior based on an object's type, and new types are expected
  • Null checks for the same object in multiple client methods

The core insight from Fowler: Conditional logic has two parts — the *switching logic* (which path to take) and the *details* of what each path does. When these are mixed inside the same method body, the reader must decode both simultaneously. The refactorings in this chapter separate these concerns so each part is named and readable on its own.

This skill depends on diagnosis. If you arrived here from code-smell-diagnosis with a Switch Statements finding, use the decision framework in Step 1 to select the right refactoring. If you have a simpler conditional problem (not a Switch Statements smell), the framework also applies — start with the pattern that matches your code structure.


Context and Input Gathering

Required Input

  • The target conditional. Either a file path + method name, or a pasted code block. Why: the selection framework requires reading the actual structure — the number of conditions, whether they share results, what the branches do, whether the type drives behavior in multiple places.
  • Whether the conditional appears in multiple places. Ask or grep. Why: a conditional that switches on a type in one method might be the right candidate for Replace Parameter with Explicit Methods; the same switch appearing in five methods is the right candidate for Replace Conditional with Polymorphism. Location multiplicity changes the prescription.

Observable Context (gather before asking)

Signals to grep for before reading the code:
  - Same variable in switch/case across multiple files: grep for the type constant names
  - Null checks: grep for "== null" or "!= null" on the same variable
  - Control flags: grep for boolean variables set to true/false inside loop bodies
  - Identical tail code in branches: visually scan each branch body for repeated statements

Process

Step 1: Classify the Conditional and Select the Refactoring

ACTION: Read the target conditional and match it to the pattern below. Use the first match that fits — the patterns are ordered from simplest to most structural.

WHY: The 8 refactorings are not interchangeable. Applying Decompose Conditional when the real problem is type-based dispatch produces a cleaner conditional that still grows incorrectly when types are added. Applying Replace Conditional with Polymorphism when there are only two stable cases and one method creates unnecessary class hierarchy. Selecting the right refactoring requires classifying the structural problem first.


Pattern 1: The condition expression itself is complex

Signal: The condition in the if statement (or the else if) is a multi-part boolean expression that requires re-reading to understand. The branch bodies may be simple. The *condition* is the hard part, not the branch logic.

Refactoring: Decompose Conditional

Extract the condition, the then-part, and the else-part into their own methods. Name the methods after *why* the branching happens, not *what* the expressions compute.

Before:
  if (date.before(SUMMER_START) || date.after(SUMMER_END))
      charge = quantity * _winterRate + _winterServiceCharge;
  else charge = quantity * _summerRate;

After:
  if (notSummer(date))
      charge = winterCharge(quantity);
  else charge = summerCharge(quantity);

Why this matters: notSummer(date) conveys the *intent* of the condition; the expression date.before(SUMMER_START) || date.after(SUMMER_END) conveys the *mechanics*. The method name reads like a comment that cannot go stale.

Note: If you find a nested conditional during Decompose Conditional, first check whether Replace Nested Conditional with Guard Clauses (Pattern 5) applies — guard clauses may eliminate the nesting before you need to decompose it.


Pattern 2: Multiple separate conditions all produce the same result

Signal: A sequence of if statements (or early return 0; checks) where each check is different but all lead to the same action. The checks feel like they belong together.

Refactoring: Consolidate Conditional Expression, then Extract Method

Step 1 — Verify no side effects exist in any condition (if side effects are present, consolidation is not safe). Step 2 — Combine the checks into a single conditional using || (or && for and-chains). Step 3 — Apply Extract Method to give the combined condition a meaningful name.

Before:
  if (_seniority < 2) return 0;
  if (_monthsDisabled > 12) return 0;
  if (_isPartTime) return 0;
  // compute disability amount

After:
  if (isNotEligibleForDisability()) return 0;
  // compute disability amount

Why this matters: The three separate checks *communicate* that they are independent decisions. They are not — they are three ways of saying "this person does not qualify." The consolidated version makes the semantic unity visible and names it. Extract Method also sets up the consolidated check to be reused or overridden cleanly.


Pattern 3: The same code appears inside every branch

Signal: Looking at each branch of the conditional, you see the same statement (or statements) at the start or end of every branch. The code executes regardless of which branch is taken.

Refactoring: Consolidate Duplicate Conditional Fragments

Move the common code to before the conditional (if it appears at the start of all branches) or after (if it appears at the end of all branches).

Before:
  if (isSpecialDeal()) {
      total = price * 0.95;
      send();
  } else {
      total = price * 0.98;
      send();
  }

After:
  if (isSpecialDeal())
      total = price * 0.95;
  else
      total = price * 0.98;
  send();

Why this matters: Code inside both branches implies the branching decision controls it. Moving it out makes clear that the conditional only determines the price multiplier — send() always happens. This reduces duplication and makes the conditional's scope accurate.

If the common code is in the middle of branches (not at start or end), check whether the code before or after it changes anything, then move it to whichever end is safe. If more than one statement is common, extract them into a method.


Pattern 4: A boolean variable tracks when to stop processing

Signal: A variable (found, done, flag) initialized to false before a loop, set to true inside the loop to signal when to stop, and checked in the loop condition or inside the loop body to skip further processing. The variable exists to work around a single-exit-point constraint, not because the logic requires it.

Refactoring: Remove Control Flag

Replace the control flag with a break or return statement.

  • If the flag only controls loop exit (no result value carried): replace flag = true with break inside the loop, then remove the flag and its condition check.
  • If the flag also carries a result value: extract the loop into its own method; replace flag = result_value with return result_value; the method returns the found value directly.
Before:
  boolean found = false;
  for (int i = 0; i < people.length; i++) {
      if (!found) {
          if (people[i].equals("Don")) { sendAlert(); found = true; }
          if (people[i].equals("John")) { sendAlert(); found = true; }
      }
  }

After (with break):
  for (int i = 0; i < people.length; i++) {
      if (people[i].equals("Don")) { sendAlert(); break; }
      if (people[i].equals("John")) { sendAlert(); break; }
  }

Why this matters: The control flag is an artifact of structured programming's one-exit-point rule. Fowler rejects this rule: "Clarity is the key principle. If the method is clearer with one exit point, use one; otherwise don't." The break or return directly expresses the intent — stop processing when the condition is met — without requiring the reader to track an extra variable's state across iterations.

Prefer the return approach (extract into a method) even in languages that support break, because return makes it clear that no further code in the method executes after the match is found.


Pattern 5: Nested conditionals hide the normal execution path

Signal: A method with nested if-else blocks where the "normal" case — the path that runs for the typical, non-exceptional input — is buried inside else clauses. Reading the method requires tracking multiple nesting levels to find the main path. The branches before the normal case handle unusual or error conditions.

Refactoring: Replace Nested Conditional with Guard Clauses

For each unusual condition, replace its else wrapper with a guard clause: a check at the top of the method that returns (or throws) immediately if the unusual condition is true. The normal path falls through to the end.

Before:
  double getPayAmount() {
      double result;
      if (_isDead) result = deadAmount();
      else {
          if (_isSeparated) result = separatedAmount();
          else {
              if (_isRetired) result = retiredAmount();
              else result = normalPayAmount();
          }
      }
      return result;
  }

After:
  double getPayAmount() {
      if (_isDead) return deadAmount();
      if (_isSeparated) return separatedAmount();
      if (_isRetired) return retiredAmount();
      return normalPayAmount();
  }

The critical semantic distinction: An if-else construct communicates that both branches are *equally likely and equally important* — the reader gives equal weight to each leg. A guard clause communicates "this is rare and exceptional — handle it and get out." The if-else form is wrong for special cases because it visually equalizes things that are not equal in the domain.

Fowler: "The guard clause says, 'This is rare, and if it happens, do something and get out.'"

Reversing conditions: When the nesting goes the other way (the method does something only when conditions are all satisfied), reverse each condition to get the guard. Negate the condition, add the guard clause with an early return, remove the outer if wrapper.

Apply guard clauses one at a time. Compile and test after each replacement.


Pattern 6: A switch (or if-else-if chain) branches on object type and new types are expected

Signal: A switch statement (or a chain of if (type == X) ... else if (type == Y)) selects different behavior depending on the type of an object. The same switch appears in multiple methods, or the type set is expected to grow. Adding a new type means finding every switch and adding a case.

Refactoring: Replace Conditional with Polymorphism

Move each leg of the conditional into an overriding method on a subclass. Make the original method abstract on the superclass.

Prerequisites: You need an inheritance hierarchy. If one does not exist, create it first using Replace Type Code with Subclasses (if the type does not change after object creation) or Replace Type Code with State/Strategy (if the type changes at runtime, or if the class is already subclassed for another reason).

Once the hierarchy exists:

  1. If the conditional is part of a larger method, use Extract Method to isolate it.
  2. Use Move Method to place the conditional on the class at the top of the hierarchy.
  3. For each leg of the conditional: copy the leg body into an overriding method on the appropriate subclass. Compile and test. Remove the copied leg from the original switch. Repeat until all legs are removed.
  4. Declare the superclass method abstract.
Before (in EmployeeType):
  int payAmount(Employee emp) {
      switch (getTypeCode()) {
          case ENGINEER:  return emp.getMonthlySalary();
          case SALESMAN:  return emp.getMonthlySalary() + emp.getCommission();
          case MANAGER:   return emp.getMonthlySalary() + emp.getBonus();
          default: throw new RuntimeException("Incorrect Employee");
      }
  }

After:
  class Engineer...    int payAmount(Employee emp) { return emp.getMonthlySalary(); }
  class Salesman...    int payAmount(Employee emp) { return emp.getMonthlySalary() + emp.getCommission(); }
  class Manager...     int payAmount(Employee emp) { return emp.getMonthlySalary() + emp.getBonus(); }
  abstract class EmployeeType...  abstract int payAmount(Employee emp);

The polymorphism principle: The caller does not need to know about the conditional behavior. Adding a new variant means adding a new class and implementing its method — the caller never changes. This is the reason object-oriented programs have fewer switch statements than procedural programs: the dispatch is handled by the language's method resolution mechanism rather than by explicit code.

When NOT to use polymorphism: If the conditional appears in only one place, affects only one method, and the type set is stable (not expected to grow), the structural investment of a hierarchy may not be justified. In that case, consider Pattern 7 (Replace Parameter with Explicit Methods) instead.


Pattern 7: A parameter controls which of several distinct operations runs, in a single method

Signal: A method takes a parameter (often a string, constant, or boolean) and uses it to select between a small number of clearly distinct operations. Each branch of the conditional does something completely different. The method's behavior is determined entirely by the parameter value, and the callers always pass a literal value (never a computed one). The type set is stable — no new variants are expected.

Refactoring: Replace Parameter with Explicit Methods

Create a separate method for each value of the parameter. Delete the conditional dispatch method. Update each call site to call the appropriate explicit method directly.

Before:
  void setValue(String name, int value) {
      if (name.equals("height")) _height = value;
      if (name.equals("width"))  _width = value;
  }
  // callers:  setValue("height", 10);  setValue("width", 5);

After:
  void setHeight(int value) { _height = value; }
  void setWidth(int value)  { _width = value; }
  // callers:  setHeight(10);  setWidth(5);

Why this matters: The explicit methods are statically checkable — the compiler catches invalid parameter names. Each method has a clear, single purpose. Callers communicate intent directly in the method name rather than encoding it in a string argument.

Condition for use: Only apply this when callers always pass a literal constant — never a variable. If callers compute the parameter value at runtime (e.g., passing a value from user input or a database), the callers need the dispatching method and you cannot eliminate it.


Pattern 8: Null checks for the same object appear in multiple client methods

Signal: Multiple methods in client code contain checks of the form if (object == null) do default thing; else object.doRealThing(). The same default behavior is repeated wherever the object might be null. The null-handling is scattered rather than centralized.

Refactoring: Introduce Null Object

Create a null version of the class that implements the same interface and returns sensible default values for all methods. Replace null with instances of this null class. Client code stops checking for null and calls methods directly.

Step 1 — Create a subclass (or implement a Nullable interface) as the null version of the source class. Add an isNull() method that returns true on the null class and false on the real class. Step 2 — Find all places that return null for the source type. Return an instance of the null class instead. Step 3 — Find all == null checks on the source type. Replace each with isNull(). Compile and test incrementally — replace one source at a time. Step 4 — For each check of the form if (obj.isNull()) defaultValue; else obj.realBehavior(): add the appropriate method to the null class returning defaultValue. Remove the condition. Client code calls obj.realMethod() unconditionally.

Before (in clients):
  if (customer == null) plan = BillingPlan.basic();
  else plan = customer.getPlan();

  if (customer == null) name = "occupant";
  else name = customer.getName();

After (NullCustomer.getName() returns "occupant", NullCustomer.getPlan() returns BillingPlan.basic()):
  plan = customer.getPlan();
  name = customer.getName();

Why this matters: The essence of polymorphism is that you ask an object to do something and it does the right thing based on its type — you do not ask what type it is first. Null objects extend this principle: the null object knows how to behave when it is absent, so the caller never needs to check. The repeated conditional is eliminated at the source, not patched at each call site.

When to prefer isNull() over null: Null objects work well when most clients want the same default behavior. Clients that need a different response can still call isNull() explicitly. Use this refactoring when the default behavior is shared across many clients.


Step 2: Apply the Selected Refactoring

ACTION: Execute the mechanics for the selected refactoring, one small step at a time. Compile and test after each step.

WHY: Conditional refactorings are behavior-preserving transformations. Testing after each individual step (not after all steps) means any regression has a minimal search space — you know exactly which change caused it. Skipping intermediate tests and making all changes at once turns a refactoring session into a debugging session.

Mechanics rule for all 8 refactorings: Use Extract Method liberally. Extracting any condition, branch body, or common fragment into a named method is always safe (it is behavior-preserving) and always improves readability. When in doubt, extract.


Step 3: Introduce Assertion for Implicit Assumptions (supporting technique)

ACTION: After refactoring the conditional, scan the remaining code for implicit assumptions — places where the code works only if some state is true, but that state is not checked or documented.

WHY: Assertions make assumptions explicit. They do not change behavior (a failing assertion produces the same exception the code would throw anyway, just closer to the source). They serve as communication: the reader immediately sees what the code requires, rather than decoding the algorithm to discover it. They also help debugging by catching violated assumptions near the violation point rather than downstream.

When to add an assertion:

  • A method assumes at least one of several fields has a non-null or non-sentinel value
  • A calculation assumes an input is positive (or within a range)
  • A method assumes an object has been initialized before being called
Before (implicit):
  double getExpenseLimit() {
      // should have either expense limit or a primary project
      return (_expenseLimit != NULL_EXPENSE) ?
          _expenseLimit : _primaryProject.getMemberExpenseLimit();
  }

After (explicit):
  double getExpenseLimit() {
      Assert.isTrue(_expenseLimit != NULL_EXPENSE || _primaryProject != null);
      return (_expenseLimit != NULL_EXPENSE) ?
          _expenseLimit : _primaryProject.getMemberExpenseLimit();
  }

Assertions should be easily removable for production deployment. Use an assertion utility class rather than live if statements for assertion logic. Do not use assertions to check things that are not truly required — over-asserting creates duplicate logic that drifts from the real code. The test: if the assertion fails and the code still works correctly, the assertion is wrong.


Key Principles

1. Separate the switching logic from the branch details. A conditional should tell you *why* branching happens. The branch bodies should tell you *what* happens in each case. When both are mixed in one method, Extract Method to split them apart. The condition becomes a method call with a meaningful name; the branch bodies become method calls with meaningful names. The if-else statement is now pure routing logic.

2. Guard clauses are semantically different from if-else. Using if-else when one branch is a special case is incorrect code communication — it tells the reader both paths are equally probable and important. Guard clauses (early returns) correctly communicate "this is exceptional; handle it and exit." Fowler explicitly rejects the single-exit-point rule: "Clarity is the key principle."

3. Adding types should not require finding conditionals. If adding a new variant of a type requires searching the codebase for every switch on that type and adding a case, Replace Conditional with Polymorphism. The polymorphic design means adding a new type = adding a new class that implements the behavior. No existing code changes. The caller does not need to know about type-specific conditional behavior.

4. Null objects eliminate scattered default behavior. Null checks are repeated default-behavior decisions. When many callers share the same default, the decision belongs in the object, not in every caller. The null object embodies the default; callers stop making the decision.

5. Apply one refactoring at a time, test between each. Conditional refactorings often chain — Consolidate Conditional Expression followed by Extract Method; Replace Conditional with Polymorphism preceded by Replace Type Code with Subclasses. Apply each step independently, verify behavior is preserved, then proceed. The chain is safe; the leap is not.


Examples

Example 1: Choosing Between Polymorphism and Explicit Methods

Scenario: A Shape class has this method:

double area(String shapeType) {
    if (shapeType.equals("circle"))    return Math.PI * _radius * _radius;
    if (shapeType.equals("rectangle")) return _width * _height;
    if (shapeType.equals("triangle"))  return 0.5 * _base * _height;
    throw new RuntimeException("Unknown shape");
}

Two questions determine the refactoring:

  1. Does shapeType vary at runtime (computed value), or is it always a literal at call sites?
  2. Are new shape types expected?

If both answers are yes: Pattern 6 — Replace Conditional with Polymorphism. Create Circle, Rectangle, Triangle subclasses; each implements area(). Adding Pentagon = adding a class.

If the type set is stable and callers always pass literals: Pattern 7 — Replace Parameter with Explicit Methods. Create circleArea(), rectangleArea(), triangleArea() methods. Simpler, statically checkable, no hierarchy overhead.

Decision rule: When types will grow or the conditional appears in multiple places → polymorphism. When types are fixed, the conditional is in one place, and callers always pass constants → explicit methods.


Example 2: Identifying a Null Object Opportunity

Scenario: Three separate methods in client code contain:

if (customer == null) plan = BillingPlan.basic();
else plan = customer.getPlan();

if (customer == null) name = "occupant";
else name = customer.getName();

if (customer == null) weeksDelinquent = 0;
else weeksDelinquent = customer.getHistory().getWeeksDelinquentInLastYear();

Classification: Pattern 8 — the same object (customer) is null-checked in multiple clients, each providing a sensible default.

Apply Introduce Null Object:

class NullCustomer extends Customer {
    public boolean isNull()          { return true; }
    public String getName()          { return "occupant"; }
    public BillingPlan getPlan()     { return BillingPlan.basic(); }
    public PaymentHistory getHistory() { return PaymentHistory.newNull(); }
}
// class NullPaymentHistory: getWeeksDelinquentInLastYear() returns 0

// Site returns NullCustomer instead of null:
Customer getCustomer() {
    return (_customer == null) ? Customer.newNull() : _customer;
}

// Clients become:
plan            = customer.getPlan();
name            = customer.getName();
weeksDelinquent = customer.getHistory().getWeeksDelinquentInLastYear();

The three conditional blocks disappear. Note that null objects often return other null objects — NullCustomer.getHistory() returns NullPaymentHistory, which itself returns sensible defaults.


References

FileContentsWhen to read
references/refactoring-prescriptions.mdFull prescription tree for all conditional refactorings with Fowler chapter referencesVerifying the correct conditional branch for a borderline case

Dependency skill:

  • code-smell-diagnosis — diagnoses the Switch Statements smell and other structural problems that trigger this skill; provides the prioritized finding that this skill executes

Related skills in the refactoring set:

  • type-code-refactoring-selector — when the conditional is driven by a type code and the primary decision is how to restructure the type (Replace Type Code with Subclasses vs. State/Strategy), not just the conditional dispatch
  • method-decomposition-refactoring — when Long Method is the primary smell and conditional complexity is one contributor among several

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-type-code-refactoring-selector
  • clawhub install bookforge-method-decomposition-refactoring

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

平台分布

OpenClaw

83.11%
按下载量换算1,129

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

通过

权限和风险

调用外部服务

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

安装前确认

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

继续浏览同类 Skills