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

如何避免类中的代码重复?基于抽象类继承场景

Avoiding Code Duplication in Subclass Save Methods

Great question! When you’ve got multiple subclasses sharing similar implementation logic for methods like _save(), there are a few clean approaches to eliminate repetition while keeping your code maintainable. Let’s walk through the most practical options for your scenario:

Option 1: Extract Common Logic to the Abstract Parent Class

The simplest approach is to move shared _save() functionality into your SubstitutionAbstract base class, then let subclasses override or extend it as needed. Here’s how you could refactor:

abstract class SubstitutionAbstract {
  // Extract shared data construction logic into a protected helper
  protected buildBaseSubstitutionData(): Partial<ISubstitutionModelTeacher> {
    return {
      lesson: this.substitution.lessonNumber,
      confirmed: true,
      subsDate: this.substitution.date,
      // Add all other common fields here
    };
  }

  // Define the abstract method to enforce implementation in subclasses
  public abstract _save(): void;
}

class SubstitutionFree extends SubstitutionAbstract {
  public _save(): void {
    // Start with the base data from the parent
    const substitutionAdd = this.buildBaseSubstitutionData();
    // Add subclass-specific fields if needed
    substitutionAdd.type = "free";
    
    // Execute the save operation (e.g., API call)
    api.save(substitutionAdd);
  }
}

class SubstitutionSubject extends SubstitutionAbstract {
  public _save(): void {
    const substitutionAdd = this.buildBaseSubstitutionData();
    // Add Subject-specific fields
    substitutionAdd.subject = this.substitution.newSubject;
    
    api.save(substitutionAdd);
  }
}

// For SubstitutionTeacher, implement a completely custom save logic
class SubstitutionTeacher extends SubstitutionAbstract {
  public _save(): void {
    const teacherSubData = {
      lesson: this.substitution.lessonNumber,
      teacherId: this.substitution.newTeacherId,
      // Custom fields for teacher substitutions
    };
    
    api.saveTeacherSubstitution(teacherSubData);
  }
}

This keeps shared code centralized, making it easy to update later without touching every subclass.

Option 2: Use Mixins for Reusable Functionality

If you want to keep the parent class focused and avoid cluttering it with save logic, mixins let you inject reusable _save() behavior into only the subclasses that need it:

// Define a constructor type for mixins
type Constructor<T = {}> = new (...args: any[]) => T;

// Create a mixin with the base save logic
function SubstitutionSaveMixin<TBase extends Constructor<SubstitutionAbstract>>(Base: TBase) {
  return class extends Base {
    public _save(): void {
      const substitutionAdd = {
        lesson: this.substitution.lessonNumber,
        confirmed: true,
        subsDate: this.substitution.date,
      };
      
      // Hook for subclasses to modify data before save
      this.beforeSave(substitutionAdd);
      
      api.save(substitutionAdd);
    }

    // Default empty hook (subclasses can override this)
    protected beforeSave(data: Partial<ISubstitutionModelTeacher>): void {}
  };
}

// Apply the mixin to SubstitutionFree
class SubstitutionFree extends SubstitutionSaveMixin(SubstitutionAbstract) {
  protected beforeSave(data: Partial<ISubstitutionModelTeacher>): void {
    data.type = "free";
  }
}

// Apply the mixin to SubstitutionSubject
class SubstitutionSubject extends SubstitutionSaveMixin(SubstitutionAbstract) {
  protected beforeSave(data: Partial<ISubstitutionModelTeacher>): void {
    data.subject = this.substitution.newSubject;
  }
}

// SubstitutionTeacher still implements its own save logic
class SubstitutionTeacher extends SubstitutionAbstract {
  public _save(): void {
    // Custom teacher-specific save logic here
  }
}

Mixins are great when you want to reuse logic across unrelated classes later on.

Option 3: Strategy Pattern for Flexible Data Construction

If the main difference between subclasses is how they construct the substitution data (rather than the save process itself), the strategy pattern lets you decouple data building from the save method:

// Define a strategy interface for data construction
interface SubstitutionDataStrategy {
  buildData(substitution: any): Partial<ISubstitutionModelTeacher>;
}

// Base strategy with shared logic
class BaseSubstitutionStrategy implements SubstitutionDataStrategy {
  buildData(substitution: any): Partial<ISubstitutionModelTeacher> {
    return {
      lesson: substitution.lessonNumber,
      confirmed: true,
      subsDate: substitution.date,
    };
  }
}

// Free-specific strategy
class FreeSubstitutionStrategy extends BaseSubstitutionStrategy {
  buildData(substitution: any): Partial<ISubstitutionModelTeacher> {
    const baseData = super.buildData(substitution);
    return { ...baseData, type: "free" };
  }
}

// Subject-specific strategy
class SubjectSubstitutionStrategy extends BaseSubstitutionStrategy {
  buildData(substitution: any): Partial<ISubstitutionModelTeacher> {
    const baseData = super.buildData(substitution);
    return { ...baseData, subject: substitution.newSubject };
  }
}

// Update the abstract parent to use a strategy
abstract class SubstitutionAbstract {
  protected dataStrategy: SubstitutionDataStrategy;

  constructor(strategy: SubstitutionDataStrategy) {
    this.dataStrategy = strategy;
  }

  public _save(): void {
    const substitutionAdd = this.dataStrategy.buildData(this.substitution);
    api.save(substitutionAdd);
  }
}

// Initialize each subclass with its strategy
class SubstitutionFree extends SubstitutionAbstract {
  constructor() {
    super(new FreeSubstitutionStrategy());
  }
}

class SubstitutionSubject extends SubstitutionAbstract {
  constructor() {
    super(new SubjectSubstitutionStrategy());
  }
}

class SubstitutionTeacher extends SubstitutionAbstract {
  constructor() {
    super({
      buildData: (substitution) => ({
        lesson: substitution.lessonNumber,
        teacherId: substitution.newTeacherId,
        // Custom teacher data fields
      })
    });
  }
}

This pattern makes it easy to add new substitution types later without modifying existing code—just create a new strategy class.

Which Option to Choose?

  • Option 1 is best for simple cases where most subclasses share core save logic.
  • Option 2 works well if you want to keep the parent class lean and reuse the save logic elsewhere.
  • Option 3 is ideal if your substitution types vary mostly in how they construct data, and you want maximum flexibility.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:53:16