遗留项目中MyClass字符串修改的设计模式优化方案咨询
问题描述
我正在维护一个遗留项目,简化后的场景如下:
存在一个10000行代码的MyClass类,核心定义如下:
public class MyClass { private String myString; // 更多成员变量,遗留类共10000行代码 public String getMyString() { return myString; } public void setMyString(String myString) { this.myString = myString; } }
另外有一个跨包的MyStringHelper类,通过静态方法修改MyClass实例的myString:
public class MyStringHelper { public static void modifyMyString(MyClass myClass) { // 对myClass的myString做一些处理 myClass.setMyString("BLABLA"); } public static void modifyMyStringII(MyClass myClass) { // 对myClass的myString做一些处理 String old = myClass.getMyString(); myClass.setMyString(old + "BLABLA"); } }
当前实现依赖开发者仅通过MyStringHelper修改myString,不能直接调用MyClass::setMyString,但这种约束完全靠自觉,很容易出错。
想请教:从设计模式角度,有没有优雅的解决方案?另外,封装一个MyString类的方案是否更优?该类示例定义如下:
public class MyString { private String myString; public MyString(Trans trans) { this.myString = myString; } public String getMyString() { return myString; } public void modifyMyString() { ... } public void modifyMyStringII() { ... } }
解决方案
方案1:将修改逻辑内聚到MyClass(优先适配遗留代码)
既然MyStringHelper的逻辑是专门针对MyClass的myString,最直接的做法是把这些行为迁移到MyClass内部,同时逐步限制setMyString的公开访问:
- 在
MyClass中新增对应修改方法,并标记旧方法为废弃:
public class MyClass { private String myString; // 其他遗留代码 public String getMyString() { return myString; } // 标记为废弃,明确提示开发者不要直接调用 @Deprecated public void setMyString(String myString) { this.myString = myString; } // 迁移MyStringHelper的逻辑到这里 public void modifyMyString() { // 原Helper里的处理逻辑 this.myString = "BLABLA"; } public void modifyMyStringII() { // 原Helper里的处理逻辑 this.myString = this.myString + "BLABLA"; } }
- 修改
MyStringHelper的静态方法,内部调用MyClass的新方法,同样标记为废弃,引导开发者直接使用MyClass的方法:
public class MyStringHelper { @Deprecated public static void modifyMyString(MyClass myClass) { myClass.modifyMyString(); } @Deprecated public static void modifyMyStringII(MyClass myClass) { myClass.modifyMyStringII(); } }
优势:
- 改动量极小,不用大规模修改遗留代码
- 符合封装原则,数据和操作数据的行为归为一体
- 通过
@Deprecated平滑过渡,避免硬切换导致的编译错误
方案2:封装MyString值对象(彻底重构)
如果myString的修改逻辑复杂,且后续有扩展需求,封装成值对象是更彻底的优化方案,符合单一职责原则:
- 完善
MyString类,把所有修改逻辑内聚到自身(修正原示例构造函数的参数问题):
public class MyString { private String value; public MyString(String initialValue) { this.value = initialValue; } public String getValue() { return value; } public void modifyToBlabla() { this.value = "BLABLA"; } public void appendBlabla() { this.value += "BLABLA"; } // 可选:设计成不可变对象,更符合值对象规范 public MyString appendBlablaImmutable() { return new MyString(this.value + "BLABLA"); } }
- 修改
MyClass,用MyString替代原有的String成员,移除直接修改字符串的setMyString:
public class MyClass { private MyString myString; // 其他遗留代码 public MyString getMyString() { return myString; } // 仅保留初始化或替换整个MyString的方法(如果需要) public void setMyString(MyString myString) { this.myString = myString; } }
- 使用方式变为:
MyClass myObj = new MyClass(); myObj.getMyString().modifyToBlabla(); // 不可变版本的调用方式 myObj.setMyString(myObj.getMyString().appendBlablaImmutable());
优势:
- 彻底封装
myString的所有操作,杜绝直接修改底层字符串的可能 - 扩展性强,新增修改逻辑只需在
MyString中添加方法 - 不可变设计能避免并发场景下的线程安全问题
注意事项:
- 对遗留代码侵入性较大,需要修改所有直接调用
getMyString()获取字符串的地方(比如改成myObj.getMyString().getValue()) - 建议分阶段重构,先保留
getMyString()返回String的兼容方法,再逐步切换
方案对比
- 若只是快速解决“开发者直接调用setMyString”的问题,优先选方案1,改动小、风险低
- 若
myString业务逻辑复杂且有长期扩展需求,选方案2,架构更清晰、符合面向对象设计原则
内容的提问来源于stack exchange,提问作者AndreasInfo
相关产品推荐
相关产品推荐

