You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何消除继承场景下的代码重复?以Character工厂方法为例

Optimizing Character Creation with Factory Pattern

Great question! This is exactly the kind of scenario where the Factory Pattern (with a registry-based approach) shines—it eliminates repetitive if branches and makes your code way more maintainable, especially as you add dozens or hundreds of CharacterType variants.

The Problem with Your Current Code

Right now, your makeCharacter function relies on hardcoded conditional checks. Every time you add a new character type (like SNIPER or ENGINEER), you have to:

  • Open up the makeCharacter function
  • Add another if block
  • Recompile and redeploy your code

This violates the Open/Closed Principle (a core SOLID tenet)—your code should be open for extension but closed for modification.

The Optimized Solution: Registry-Based Factory

Here's how to refactor your code to wipe out all those repetitive branches:

Step 1: Define a Creator Function Type

First, create a type alias for a function that handles character creation. This keeps your code clean and reusable:

#include <functional>
#include <unordered_map>
#include <memory>

// Can be defined inside or outside your Game class, based on scope needs
using CharacterCreator = std::function<std::shared_ptr<mtm::Character>(
    mtm::Team, 
    mtm::units_t, 
    mtm::units_t, 
    mtm::units_t, 
    mtm::units_t
)>;

Step 2: Create a Static Registry Map

Next, set up a static map that links each CharacterType to its corresponding creation logic. Initialize this once (e.g., in a static helper or the Game class constructor):

class Game {
private:
    // Static registry: maps CharacterType to its creator function
    static std::unordered_map<mtm::CharacterType, CharacterCreator> character_creators;

    // Helper to initialize the registry (runs once at program start)
    static void initCharacterRegistry() {
        character_creators[mtm::SOLDIER] = [](mtm::Team team, mtm::units_t health, mtm::units_t ammo, mtm::units_t range, mtm::units_t power) {
            return std::make_shared<mtm::Soldier>(team, health, ammo, range, power);
        };
        character_creators[mtm::MEDIC] = [](mtm::Team team, mtm::units_t health, mtm::units_t ammo, mtm::units_t range, mtm::units_t power) {
            return std::make_shared<mtm::Medic>(team, health, ammo, range, power);
        };
        // Add new character types here later—no need to touch makeCharacter!
    }

public:
    // Ensure the registry is initialized before first use
    Game() {
        static bool registry_initialized = false;
        if (!registry_initialized) {
            initCharacterRegistry();
            registry_initialized = true;
        }
    }

    std::shared_ptr<mtm::Character> makeCharacter(mtm::CharacterType type, mtm::Team team, mtm::units_t health, mtm::units_t ammo, mtm::units_t range, mtm::units_t power);
};

// Initialize the static map outside the class
std::unordered_map<mtm::CharacterType, CharacterCreator> Game::character_creators;

Step 3: Simplify makeCharacter

Now your makeCharacter function becomes lean and focused—no more endless if blocks:

std::shared_ptr<mtm::Character> Game::makeCharacter(mtm::CharacterType type, mtm::Team team, mtm::units_t health, mtm::units_t ammo, mtm::units_t range, mtm::units_t power) {
    if (health <= 0 || ammo < 0 || range < 0 || power < 0) {
        throw mtm::IllegalArgument();
    }

    auto creator_it = character_creators.find(type);
    if (creator_it == character_creators.end()) {
        // Optional: Throw a custom exception like mtm::UnknownCharacterType instead
        throw mtm::IllegalArgument();
    }

    // Delegate creation to the registered function
    return creator_it->second(team, health, ammo, range, power);
}

Why This Works Better

  • No More Repetition: Adding a new character type only requires adding one line to the initCharacterRegistry function (or even dynamically, see below).
  • Complies with Open/Closed Principle: You never need to modify makeCharacter again—extend by adding new entries to the registry instead.
  • Cleaner, More Readable Code: All creation logic is centralized in the registry, making it easier to debug and maintain.

Bonus: Dynamic Registration (For Large Codebases)

If your character types are spread across different modules or plugins, add a public method to register new creators at runtime:

void Game::registerCharacterType(mtm::CharacterType type, CharacterCreator creator) {
    character_creators[type] = creator;
}

This lets external code (like a plugin) register new character types without touching the core Game class at all.

Quick Note on std::make_shared

I swapped your std::shared_ptr<Character>(new mtm::Soldier(...)) with std::make_shared<mtm::Soldier>(...)—this is safer (avoids potential memory leaks if an exception is thrown between new and assigning to the shared_ptr) and more efficient (allocates the control block and object memory in a single chunk).

内容的提问来源于stack exchange,提问作者user13812739

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 11:42:29