Java封装&;面向对象的最佳实践
我实现了一个名为mobileCall的类。我从这个类中创建了几个对象,并使用来自一个XML的值填充这个对象的字符串变量,该XML具有针对特定人员的几个MobileCall。我需要对此人拨打的所有电话进行分组和计数(即国内电话:11分钟;国际电话:15分钟;数据:20 MB) 因此,我在类中实现了几个公共方法来检查调用的类型,以返回true或false。在主类中,我调用了这些方法来检查它们是否满足标准,我计算了具体的计数器 有一位专业人士看到了我的代码,说这不是一个好的实践,OOP的设计就是为了消除这种“what is you”方法。还有更好的方法来实现这种行为。我试图通读OOP和封装,但找不到更好的方法。我觉得他说得有道理 代码示例Java封装&;面向对象的最佳实践,java,oop,encapsulation,Java,Oop,Encapsulation,我实现了一个名为mobileCall的类。我从这个类中创建了几个对象,并使用来自一个XML的值填充这个对象的字符串变量,该XML具有针对特定人员的几个MobileCall。我需要对此人拨打的所有电话进行分组和计数(即国内电话:11分钟;国际电话:15分钟;数据:20 MB) 因此,我在类中实现了几个公共方法来检查调用的类型,以返回true或false。在主类中,我调用了这些方法来检查它们是否满足标准,我计算了具体的计数器 有一位专业人士看到了我的代码,说这不是一个好的实践,OOP的设计就是为了消
public class MobileCall {
String callType;
String callDuration;
String callAmount;
String callerID;
String calleID;
....
public boolean isNational(){
if (callType.compareTo("National")==0)
return true;
else
return false;
}
public boolean isInternational(){
if (callType.compareTo("international")==0)
return true;
else
return false;
}
...
}
In Main Method
int nationalCounter;
int internationalCounter;
MobileCall mobileCall = new MobileCall();
if(mobileCall.isNational())
nationalCounter = nationalCounter + mobileCall.getCallDuration();
else if (mobileCall.isInternational())
internationalCounter = internationalCounter + mobileCall.getDuration();
....
首先,所有实例变量都应该声明为
private
。这封装了特定类的每个实例变量或属性。这就是封装背后的全部思想,在封装中,对象的属性只能在特定的类中访问
例如,更改stringcalltype代码>到私有字符串调用类型代码>
将实例变量声明为private的优点是,可以防止其他类操纵特定对象的属性,并且可以提供安全网,使它们不会设置为错误的值。想象一下,如果一个人有能力改变另一个人的身体属性;那绝对是荒谬的。同样,属性应该是私有的,以遵循封装原则
访问属性/实例变量的方式是通过使用setter和getter。e、 g:
public void setCallType(String callType) {
this.callType = callType;
}
public String getCallType() {
return this.callType;
}
这样,您就坚持了面向对象的封装原则。这里有很多问题。。。如果你谈论一个好的设计,你可能需要重新编码这个东西。要指出几个问题:
我将尝试向您解释封装的含义,这似乎是您的基本疑问:
封装意味着:我有一个目标,我被设计来执行一项工作,除了我的工作,我什么都不知道
我不允许他人在未通知我的情况下更改我的财产,我对我的财产拥有完全控制权,我对你从我身上得到的一切负全部责任
给我任何东西
更清楚地说,一个对象必须执行一项任务,不能尝试做很多事情
现在,您如何实现上述行为
您需要限制对属性的访问,为属性提供适当/受控的访问方法
使调用类型成为枚举,因为它们是类型和值安全检查的最佳候选对象
如果你能分享更多的代码片段,我还想写更多的建议
理解OOPS基础知识的一个很好的链接是。像isNational
这样的方法对于确定对象的状态非常好,但是如果您使用它们来确定对象的类型,那么使用
是相关代码。这里是要点(现在callType
已被强封装)
三个月后,有人抱怨这个程序占用了太多内存。您重新检查了代码,认为字符串可能不是确定调用国籍的最佳方法,毕竟它不是国际性的,对吗?这意味着您最多需要一位来存储数据
你来做改变
private boolean nationalCall;
然后更改使用这些数据的方法
public boolean isNational() {
return nationalCall;
}
public boolean isInternational() {
return !nationalCall;
}
而且,由于您正确地封装了“全国通话数据”,因此您可以放心,不再需要搜索程序的其余部分,以确保程序的其他部分能够正常工作。更改的“效果”被“封装”在类的“接口”的“边界”处,该接口实际上由许多“成员方法”实现
现在像这样编码
if (mobileCall.isNational()) {
nationalCounter = nationalCounter + mobileCall.getCallDuration();
} else if (mobileCall.isInternational()) {
internationalCounter = internationalCounter + mobileCall.getDuration();
}
看起来很可疑。请注意,这段代码似乎非常关注不同类的数据和方法。也许是外部管理类到了行为位置错位的程度
如果您将这些方法添加到“Call”类中
然后您可以将累积代码减少到
while (Call call : client.getAllCalls()) {
internationalMinutes += call.getInternationalMinutes();
nationalMinutes += call.getNationalMinutes();
}
注意,这最后一点工作并不是严格意义上的100%封装,因为我们添加了接口项(方法);但是它确实做了一些行为意义上的封装,因为我们不再需要根据一系列确定类内部状态的查询来确定调用分钟是国际的还是国内的
对一个类进行一系列检查以确定如何使用该类的日期,这是该行为在“Call”类之外的外部迁移。因此,将此行为移回到类中可能被视为类行为的封装,与前面的示例不同,它是对类数据的严格封装
很多时候,人们忘记了类封装了数据和行为。感谢您找到一个很好的例子,在这个例子中,两个封装点都可以在同一个问题中出现。如果您想将本地电话与国际电话分开(或在更多国家/地区组中分开国际电话),那么它的可扩展性不强。从OO的角度来看,您可以创建MobileCall的子类型,但这将是一种过分的做法(特别是在没有与不同类型相关联的功能的情况下)
我会创建一个枚举
public enum CallType { NATIONAL, INTERNATIONAL, DATA, ... }
并将您的字符串标识符映射到枚举值(如果您选择得当,您不必为此做太多)。创建一个MobileCallTally clas
public int getNationalMinutes() {
if (national) {
return callDuration;
}
return 0;
}
public int getInternationalMinutes() {
if (!national) {
return callDuration;
}
return 0;
}
while (Call call : client.getAllCalls()) {
internationalMinutes += call.getInternationalMinutes();
nationalMinutes += call.getNationalMinutes();
}
public enum CallType { NATIONAL, INTERNATIONAL, DATA, ... }
mobileCallTally.inc(call.getType(), call.getAmount());