2

我实现了一个名为 mobileCall 的类。我从这个类创建了几个对象,并使用来自 XML 的值填充这个对象的 String 变量,该 XML 具有针对特定人员的多个 mobileCalls。我需要对这个人拨打的所有电话进行分组和计数(即国内电话: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();
....
4

5 回答 5

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% 封装,因为我们添加了接口项(方法);但它确实在行为意义上做了一些封装,因为我们不再需要根据一系列查询来确定通话时间是国际还是国内来确定班级的内部状态。

对一个类进行一系列检查以确定如何使用该类的日期是“调用”类之外的行为的外部迁移。所以将这种行为移回类可以认为是对类行为的封装,不像前面的例子,它是对类数据的严格封装。

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

于 2013-09-26T16:48:56.667 回答
1

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

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

封装意味着:我有一个目的,我被设计来执行一项工作,除了我的工作我什么都不知道。

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

在这里我是任何对象。

只是为了更清楚一点,一个对象必须执行一项工作,并且不能尝试做很多很多事情。

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

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

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

如果您可以分享更多代码片段,我还有更多建议要写。

了解 OOPS 基础知识的一个很好的链接在这里

于 2013-09-26T16:44:39.033 回答
1

像这样的方法isNational非常适合确定对象的状态,但是如果您使用它们来确定对象的类型,那么使用继承的类型可能会更好。

public class NationalMobileCall extends MobileCall 
{
}
于 2013-09-26T16:46:44.727 回答
1

好吧,如果您想将本地电话与国际电话分开(或在更多国家/地区组中分开国际电话),它的扩展性不是很好。从 OO 的角度来看,您可以创建 MobileCall 的子类型,但这将是一种矫枉过正(尤其是在没有与不同类型关联的功能的情况下)。

我会创建一个枚举

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

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

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

(当然,您可以只使用字符串作为键并完全避免枚举,但由于它是一种“类型”,我个人喜欢将其“输入”。)

于 2013-09-26T16:52:36.003 回答
0

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

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

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

可以访问属性/实例变量的方式是通过使用 setter 和 getter。例如:

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

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

这样,您就遵守了 OOP 的封装原则。

于 2013-09-26T16:31:47.947 回答