
本文讲解如何通过添加公共方法(如复制构造器、除法运算等)让weight类完全自主管理内部状态,避免project类直接访问私有成员,从而严格遵守面向对象的封装原则。
本文讲解如何通过添加公共方法(如复制构造器、除法运算等)让weight类完全自主管理内部状态,避免project类直接访问私有成员,从而严格遵守面向对象的封装原则。
在Java面向对象编程中,封装的核心在于“隐藏实现细节,暴露安全接口”。你当前遇到的问题——Project类无法访问Weight类中的private字段OUNCES_IN_A_POUND和私有方法toOunces()——并非设计缺陷,而是封装机制在正常工作。关键不在于绕过它,而在于增强Weight类的公共契约,使其能以受控方式完成所有必要计算。
✅ 正确做法:将逻辑内聚到Weight类中
原始Project.findAverage()方法试图直接拆解Weight的内部单位(调用私有toOunces()、硬编码OUNCES_IN_A_POUND),这破坏了封装。理想方案是:所有重量相关的数学运算,均由Weight自身提供公开、语义清晰的方法支持。
为此,需为Weight类补充以下两个关键能力:
1. 添加复制构造器(Copy Constructor)
用于创建现有重量的独立副本,避免意外共享状态:
/**
* 构造一个与指定Weight对象值相同的新实例
*/
public Weight(Weight other) {
if (other == null) {
throw new IllegalArgumentException("Other weight cannot be null");
}
this.pounds = other.pounds;
this.ounces = other.ounces;
// normalize() 可选,因源对象已规范,但调用更健壮
this.normalize();
}
2. 添加divideBy(double divisor)方法
封装除法逻辑,内部使用toOunces()并重新构建Weight:
/**
* 返回本重量除以指定因子后的新Weight实例
* @param divisor 除数(必须 > 0)
* @return 新的Weight对象,表示平均值
*/
public Weight divideBy(double divisor) {
if (divisor <blockquote><p>? 注意:divideBy应返回新对象(不可变风格更安全),而非修改this——这符合Weight作为值对象的设计直觉。</p></blockquote><h4>3. 优化addTo为add(语义更准确)</h4><p>原addTo(Weight w)实际是“将w加到当前实例”,但名称易误解为“向参数w添加”。建议重命名为add,并在文档中明确说明:</p><pre class="brush:php;toolbar:false;">/**
* 将给定重量加到当前重量上,并返回更新后的自身(支持链式调用)
* @param other 要相加的重量(非null)
* @return this(便于链式调用)
*/
public Weight add(Weight other) {
double thisOunces = this.toOunces();
double otherOunces = other.toOunces();
double totalOunces = thisOunces + otherOunces;
this.pounds = (int) (totalOunces / OUNCES_IN_A_POUND);
this.ounces = totalOunces % OUNCES_IN_A_POUND;
this.normalize();
return this;
}✅ 更新后的Project类(完全不触碰私有成员)
public class Project {
private static Weight findMinimum(Weight w1, Weight w2, Weight w3) {
Weight min = w1;
if (w2.lessThan(min)) min = w2;
if (w3.lessThan(min)) min = w3;
return min;
}
private static Weight findMaximum(Weight w1, Weight w2, Weight w3) {
Weight max = w1;
if (w2.lessThan(max) == false) max = w2; // 利用已有lessThan
if (w3.lessThan(max) == false) max = w3;
return max;
}
private static Weight findAverage(Weight w1, Weight w2, Weight w3) {
Weight sum = new Weight(w1); // 复制w1
sum.add(w2).add(w3); // 链式调用
return sum.divideBy(3.0); // 封装除法
}
public static void main(String[] args) {
Weight w1 = new Weight(11, 3);
Weight w2 = new Weight(7, 20);
Weight w3 = new Weight(14, 6);
System.out.println("Weight 1: " + w1);
System.out.println("Weight 2: " + w2);
System.out.println("Weight 3: " + w3);
System.out.println("Min: " + findMinimum(w1, w2, w3));
System.out.println("Max: " + findMaximum(w1, w2, w3));
System.out.println("Avg: " + findAverage(w1, w2, w3));
}
}? 关键总结
- 封装不是阻碍,而是指导:当外部类需要某类数据时,应由该类提供行为(方法),而非暴露状态(字段/私有方法)。
- 单一职责原则:Weight负责所有重量运算;Project仅负责协调逻辑。
- 防御性编程:新增构造器与方法均加入空值/非法参数校验。
- 可读性与可维护性:sum.add(w2).add(w3).divideBy(3) 比手动转换盎司、取模、重建对象更直观、更不易出错。
通过这种方式,你的代码不仅解决了编译错误,更体现了扎实的OOP设计思维——这正是CMIS 242课程希望你掌握的核心能力。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











