Java Encapsulation& OOP最佳实践

时间:2013-09-26 16:30:23

标签: java oop encapsulation

我实现了一个名为mobileCall的类。我从这个类创建了几个对象,并使用来自XML的值填充此对象的String变量,该XML具有针对特定人员的多个mobileCall。我需要对这个人拨打的所有电话进行分组和统计(即国内电话:11分钟;国际电话:15分钟;数据:20 MB)

因此,我在类中实现了几个公共方法来检查调用的类型以返回true或false。在主要课程中,我调用这些方法来检查它们是否符合我计算特定计数器的标准。

有人看过我的代码,并说这不是一个好习惯,而OOP旨在消除这些“你是什么”的方法。而且有更好的方法来实现这种行为。我试图阅读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();
....

5 个答案:

答案 0 :(得分:5)

private String callType;

....
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;
}

是相关代码。这就是重点(现在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();
}

看起来很可疑。请注意,这段代码似乎非常关注不同类的数据和方法。也许外部管理班级到这样的程度,以至于行为的位置错位了?

如果您将方法添加到“调用”类

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();
}

请注意,最后一点工作不是严格数据意义上的100%封装,因为我们添加了接口项(方法);但它确实在行为意义上做了一些封装,因为我们不再需要根据一系列查询来确定呼叫分钟是国际的还是国家的,以确定类的内部状态。

对类进行一系列检查以确定如何使用类的日期是“Call”类之外的行为的外部迁移。因此,将此行为移回到类中可能被视为对类行为的封装,与之前的示例不同,后者是对类的数据的严格封装。

很多时候,人们忘记了类封装了数据行为。感谢您找到一个很好的例子,其中两个封装点都可以在同一个问题中呈现。

答案 1 :(得分:1)

这里有很多问题......如果你谈论一个好的设计,你可能需要重新编码这个东西。指出一些问题:

我将尝试向您解释封装的含义,这似乎是您的基本疑问:

封装意味着:我有一个目的,我的目的是为了完成一份工作,除了我的工作,我什么都不知道。

我不允许其他人在没有通知我的情况下更改我的财产,我完全控制我的财产,我对你从我身上得到的东西负全部责任。

在这里,我是任何对象。

为了使它更清楚,一个对象必须执行一个单独的工作,不能尝试做很多事情。

现在你如何实现上述行为?

您需要限制对属性的访问,为属性提供适当/受控的访问方法。

将呼叫类型设为枚举,因为它们是类型和价值安全检查的最佳候选者。

如果你可以共享更多代码,我还有很多建议要写。

了解OOPS基础知识的一个很好的链接是here

答案 2 :(得分:1)

isNational这样的方法对于确定对象的状态非常好,但是如果你使用它们来确定对象的类型,那么它可能会更好使用inherited type

public class NationalMobileCall extends MobileCall 
{
}

答案 3 :(得分:1)

嗯,如果您想将本地电话与国际电话分开(或在更多国家/地区组中分开国际电话),那么它的扩展性不是很高。从OO的角度来看,您可以创建MobileCall的子类型,但这将是一种过度杀伤(特别是如果没有与不同类型相关联的功能)。

我会创建一个枚举

public enum CallType { NATIONAL, INTERNATIONAL, DATA, ... } 

并将您的字符串标识符映射到枚举值(如果您明智地选择,那么您没有做太多的事情)。创建一个MobileCallTally类(由Map支持

mobileCallTally.inc(call.getType(), call.getAmount());

(当然,你可以使用字符串作为键并完全避免使用枚举,但因为它本身就是'类型'我喜欢'打字'。)

答案 4 :(得分:0)

首先,所有实例变量都应声明为private。这封装了特定类的每个实例变量或属性。这是封装背后的整个想法,其中对象的属性只能在该特定类中访问。

例如,将String callType;更改为private String callType;

将实例变量声明为私有的优点是,您可以阻止其他类操纵特定对象的属性,并且可以提供安全网,以便它们不会设置为错误值。想象一下,如果一个人有能力改变另一个人的身体属性;这绝对是荒谬的。同样,属性应该是私有的,以遵循封装原则。

可以访问属性/实例变量的方式是使用setter和getter。 e.g:

public void setCallType(String callType) {
    this.callType = callType;
}

public String getCallType() {
    return this.callType;
}

通过这种方式,您遵循封装的OOP原则。