AI Library · 知识库

# 方法与封装:受控修改计数器

学习目标:理解面向对象与模块级封装机制,通过保护内部计数器不变式掌握接收者方法、属性与访问可见性。

前置知识与范围:结构体与函数声明。演示将状态隐藏在类型边界之后,仅暴露合法操作方法。

# 问题与边界

设计一个计数器对象 Counter,保证其非递减的不变量(Invariant):

# 关键差异

语言 字段可见性控制 方法定义形态 负增量拒绝方式
Rust 字段不加 pub 默认为模块私有 impl Counter 关联函数,&mut self 接收者 返回 Result<(), &'static str>
Go 字段小写首字母即包内私有 指针接收者方法 func (c *Counter) Add(...) 返回 error
Java private 关键字 类成员方法 抛出 IllegalArgumentException
JavaScript #value 私有字段语法(ES2022) 实例方法与 getter 抛出 RangeError
TypeScript private value: number 或 #value 实例方法与 getter 抛出 RangeError
Kotlin var value: Int private set 属性与类方法 抛出 IllegalArgumentException
Python _value 惯用法或 @property self 参数方法 抛出 ValueError
C++ private: 访问标号 成员函数 抛出 std::invalid_argument

# 各语言实现

# Rust

Rustpub struct Counter {
    value: i32, // 模块外私有
}

impl Counter {
    pub fn new() -> Self {
        Counter { value: 0 }
    }

    pub fn increment(&mut self) {
        self.value += 1;
    }

    pub fn add(&mut self, delta: i32) -> Result<(), &'static str> {
        if delta < 0 {
            return Err("delta must be >= 0");
        }
        self.value += delta;
        Ok(())
    }

    pub fn value(&self) -> i32 {
        self.value
    }
}

fn main() {
    let mut c = Counter::new();
    c.increment();
    assert_eq!(c.value(), 1);

    assert!(c.add(5).is_ok());
    assert_eq!(c.value(), 6);

    assert!(c.add(-3).is_err());
    assert_eq!(c.value(), 6); // 保持原值不变

    println!("counter: {}", c.value());
}

Rust 的可见性以模块(mod)为边界,通过 &mut self 独占可变引用确保线程与数据修改的安全性。

# Go

Gopackage main

import (
	"errors"
	"fmt"
)

type Counter struct {
	value int // 首字母小写,包外无法直接访问
}

func NewCounter() *Counter {
	return &Counter{value: 0}
}

func (c *Counter) Increment() {
	c.value++
}

func (c *Counter) Add(delta int) error {
	if delta < 0 {
		return errors.New("delta must be >= 0")
	}
	c.value += delta
	return nil
}

func (c *Counter) Value() int {
	return c.value
}

func main() {
	c := NewCounter()
	c.Increment()
	if c.Value() != 1 {
		panic("mismatch")
	}

	if err := c.Add(5); err != nil || c.Value() != 6 {
		panic("add failed")
	}

	if err := c.Add(-3); err == nil || c.Value() != 6 {
		panic("negative delta should fail and preserve value")
	}

	fmt.Printf("counter: %d\n", c.Value())
}

Go 的方法接收者必须是指针 *Counter,才能就地修改实例内部字段。

# Java

Javapublic class Main {
    public static class Counter {
        private int value = 0;

        public void increment() {
            this.value++;
        }

        public void add(int delta) {
            if (delta < 0) {
                throw new IllegalArgumentException("delta must be >= 0");
            }
            this.value += delta;
        }

        public int getValue() {
            return this.value;
        }
    }

    public static void main(String[] args) {
        Counter c = new Counter();
        c.increment();
        assert c.getValue() == 1;

        c.add(5);
        assert c.getValue() == 6;

        try {
            c.add(-3);
            assert false : "should reject negative delta";
        } catch (IllegalArgumentException expected) {
            assert c.getValue() == 6; // 维持原值不变
        }

        System.out.println("counter: " + c.getValue());
    }
}

Java 经典的 private 字段配合公开 getter/mutator 构成标准的 JavaBean 封装范式。

# JavaScript

JavaScriptconst assert = {
  equal: (a, b) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
  deepEqual: (a, b) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
  throws: (fn, errType) => {
    try { fn(); throw new Error("expected throw"); } catch (e) {
      if (e.message === "expected throw") throw e;
      if (errType && !(e instanceof errType)) throw e;
    }
  }
};

class Counter {
  #value = 0; // ES2022 私有属性

  increment() {
    this.#value++;
  }

  add(delta) {
    if (delta < 0) {
      throw new RangeError("delta must be >= 0");
    }
    this.#value += delta;
  }

  get value() {
    return this.#value;
  }
}

const c = new Counter();
c.increment();
assert.equal(c.value, 1);

c.add(5);
assert.equal(c.value, 6);

assert.throws(() => c.add(-3), RangeError);
assert.equal(c.value, 6);

console.log(`counter: ${c.value}`);

ECMAScript 2022 原生私有字段 #value 具有真正的语言级不可见性,外部无法通过字符串反射直接读取。

# TypeScript

TypeScriptconst assert = {
  equal: (a: unknown, b: unknown) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
  deepEqual: (a: unknown, b: unknown) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
  throws: (fn: () => void, errType?: unknown) => {
    try { fn(); throw new Error("expected throw"); } catch (e: unknown) {
      if (e instanceof Error && e.message === "expected throw") throw e;
      if (typeof errType === "function" && !(e instanceof errType)) throw e;
    }
  }
};

class Counter {
  #value: number = 0;

  increment(): void {
    this.#value++;
  }

  add(delta: number): void {
    if (delta < 0) {
      throw new RangeError("delta must be >= 0");
    }
    this.#value += delta;
  }

  get value(): number {
    return this.#value;
  }
}

const c = new Counter();
c.increment();
assert.equal(c.value, 1);

c.add(5);
assert.equal(c.value, 6);

assert.throws(() => c.add(-3), RangeError);
assert.equal(c.value, 6);

console.log(`counter: ${c.value}`);

推荐在 TS 中优先使用原生的 #value 私有字段,因为它在编译后依然提供运行时强制隔离,而 private 关键字仅在编译期拦截。

# Kotlin

Kotlinclass Counter {
    var value: Int = 0
        private set // 外部只读,内部可写

    fun increment() {
        value++
    }

    fun add(delta: Int) {
        require(delta >= 0) { "delta must be >= 0" }
        value += delta
    }
}

fun main() {
    val c = Counter()
    c.increment()
    check(c.value == 1)

    c.add(5)
    check(c.value == 6)

    var failed = false
    try {
        c.add(-3)
    } catch (e: IllegalArgumentException) {
        failed = true
    }
    check(failed)
    check(c.value == 6)

    println("counter: ${c.value}")
}

Kotlin 支持属性的 setter 独立可见性(private set),无需手写冗余的 getter 方法。

# Python

Pythonclass Counter:
    def __init__(self) -> None:
        self._value = 0 # 惯用单下划线保护

    def increment(self) -> None:
        self._value += 1

    def add(self, delta: int) -> None:
        if delta < 0:
            raise ValueError("delta must be >= 0")
        self._value += delta

    @property
    def value(self) -> int:
        return self._value

if __name__ == "__main__":
    c = Counter()
    c.increment()
    assert c.value == 1

    c.add(5)
    assert c.value == 6

    try:
        c.add(-3)
        assert False, "should fail on negative delta"
    except ValueError:
        pass
    assert c.value == 6

    print(f"counter: {c.value}")

Python 依靠命名约定与 @property 装饰器实现受控暴露;虽然可通过 _value 访问,但契约层面明确约定为私有。

# C++

C++#include <iostream>
#include <stdexcept>
#include <cassert>

class Counter {
private:
    int value_{0};

public:
    void increment() {
        value_++;
    }

    void add(int delta) {
        if (delta < 0) {
            throw std::invalid_argument("delta must be >= 0");
        }
        value_ += delta;
    }

    int value() const {
        return value_;
    }
};

int main() {
    Counter c;
    c.increment();
    assert(c.value() == 1);

    c.add(5);
    assert(c.value() == 6);

    try {
        c.add(-3);
        assert(false);
    } catch (const std::invalid_argument&) {
        assert(c.value() == 6);
    }

    std::cout << "counter: " << c.value() << "\n";
}

C++ 通过 const 成员函数标明只读方法,编译器严格拦截在 const 成员方法中修改字段的行为。

# 迁移误区与适用限制

# 运行与验证

语言版本基线见示例基线。 所有程序运行后控制台输出均为:

纯文本counter: 6

自增、合法累加与非法负增量拒绝(且维持原值)测试全部通过。

# 相关阅读