通过代码了解OOP中的封装

时间:2017-03-23 16:01:05

标签: java oop encapsulation

试图理解封装的概念,我遇到了这个定义"在同一实体中组合属性和方法,以隐藏应隐藏的内容并使可见的内容可见#&# 34。

但是练习相同,我不确定以下哪个代码更适合OOP:

public class Square {

    //private attribute
    private int square;

    //public interface
    public int getSquare(int value) {
        this.square = value * value;
        return this.square;
    }
}

public class Square {

    //private attribute
    private int square;

    //public interface
    public int getSquare(int value) {
        this.square = calculateSquare(value);
        return this.square;
    }

    //private implementation
    private int calculateSquare(int value) {
        return value * value;
    }
}

3 个答案:

答案 0 :(得分:2)

  

在同一实体中组合属性和方法,以便隐藏应隐藏的内容并使可见内容可见

这是一个可能具有误导性的陈述。你不是hiding来自任何人的任何东西。它也不是关于方法或领域。不幸的是,这几乎是每个地方的措辞。

如何

在编写任何程序时,(无论是函数,类,模块还是库),我们都会将 我的代码 视为我们正在处理的文章,其他所有代码都是 我的客户 代码。现在假设所有客户端代码都是由其他人编写的,而不是你。您只需编写代码。只要假设这一点,即使你是唯一一个从事整个项目的人。

现在客户端代码需要与我的代码进行交互。所以我的代码应该很好,很体面。封装的概念说,我将我的代码分为两部分,(1)客户端代码应该被打扰,(2)客户端代码不应该被打扰。实现封装的OO方式是使用公共和私有等关键字。实现这一目标的非OO方式是引用下划线等命名约定。请记住,您没有隐藏,只是将其标记为none-of-your-business

为什么

那么我们为什么要封装东西呢?什么应该将我的代码组织到公共和私人区域?当someone使用我的代码时,他们当然会使用整个内容,而不仅仅是公共内容,那么私有内容是none-of-their-business的内容?请注意,像someonetheir这样的词可以引用自己 - 但仅限于处理另一段代码时。

答案是易测试性和可维护性。一个完整的项目,如果进行详尽的测试,可能是一项非常重要的任务。至少,当您完成编码时,您只需测试我的代码的公共方面。您不测试任何客户端代码,也不测试我的代码的任何私有方面。这可以减少测试工作量,同时保留足够的覆盖范围。

另一方面是可维护性。 我的代码永远不会完美,需要修改。无论是因为修正错误还是增强功能,我的代码都需要修补。因此,当新版本的我的代码可用时,客户端代码会受到多大影响?无,如果更改在私有区域。此外,在计划变更时,我们尽量在私人区域限制它。因此,从客户的角度来看,这种变化变得毫无影响。我的代码的公共方面的变化几乎总是需要更改客户端代码,现在需要测试。在规划我的代码的大局时,我们尝试最大化私有区域下的区域,并最小化公共区域下的区域。

还有更多

将链接封装为抽象概念的想法,而抽象概念又与多态性的概念相关联。这些都不是严格意义上的OO。即使在非OO世界,如C或甚至大会,这些都适用。实现这些的方式不同。即使这适用于计算机以外的东西。

污水管理过程,例如,

在排水沟的公共界面中

封装。一般公众只对排水沟感到困扰。处理,处置,回收都不属于一般公众的业务。因此,污水管理可以被视为 -

抽象实体 - 只有排水沟的界面。不同的政府和公司以自己的方式实施这一点。现在一个城市可能有一个永久的污水管理系统,或者它可以定期 -

切换提供商。在政府运作的五十年中,情况很糟糕,但一旦他们与BigCorp公司签约,现在人们可以自由呼吸。我们刚刚做了多态性。我们切换实现,保持公共接口相同。政府和BigCorp公司都使用相同的排水渠,但是他们自己的加工设施,这些设施被封装起来并且可以多态切换。

在您的代码中

在您选择封装存储的两个代码中,该字段都是私有的。这是一个很好的方法,当然是OO方式。在两个代码中,算法也被封装 - 即客户端不可见。尼斯。在第二个代码中,您继续使用单独的非公共方法提取算法。这是值得称赞的方法,虽然显然是做一些微不足道的事情。不过没有更好的OO。

你在第二段代码中所做的甚至有一个名字:strategy pattern。即使在这里它是无用的(并且是矫枉过正的),但在一个场景中它可能是有用的,比如说你正在处理非常大的数字,这样计算它们的方块需要很长时间。在这种情况下,您可以使calculateSquare方法受到保护,拥有类FastButApproxSquare extends Square,并使用不同的算法覆盖calculateSquare方法,以便更快地计算近似值。这样你就可以做多态性。任何需要精确值的人都将使用Square类。任何需要大约值的人都会使用FastButApproxSquare类。

答案 1 :(得分:1)

封装是关于隐藏客户端代码的实现和结构细节。另外,它是关于连贯性:保持彼此紧密相关的事物。

例如,考虑一个管理足球队球员的班级:

public class FootballTeam {
    public final List<Player> players = new ArrayList<>();

}

客户端代码可以访问玩家列表,查找,添加玩家等等:

public class FootballManager {

    private final FootballTeam team = new FootballTeam();

    public void hirePlayer(Player player) {
        team.players.add(player);
    }

    public void firePlayer(int jerseyNo) {
        Optional<Player> player = team.players.stream()
                .filter(p -> p.getJerseyNo() == jerseyNo)
                .findFirst();
        player.ifPresent(p -> team.players.remove(p));
    }
}

现在,如果有人决定将字段FootballTeam.players更改为Map<Integer, Player>,将玩家的球衣号码映射到玩家,客户端代码就会中断。

此外,客户端代码处理与玩家密切相关的方面/功能。为了保护客户端代码并确保FootballTeam实现的可变性隐藏所有实现细节,请将与玩家相关的功能保持在结构附近,代表团队并减少公共接口表面:

public class FootballTeam {

    private final Map<Integer, Player> players = new HashMap<>();

    public void addPlayer(Player player) {
        players.put(player.getJerseyNo(), player);
    }

    public Optional<Player> lookupPlayer(int jerseyNo) {
        return Optional.ofNullable(players.get(jerseyNo));
    }

    public void remove(Player player) {
        players.remove(player.getJerseyNo());
    }
}

public class FootballManager {

    private final FootballTeam team = new FootballTeam();

    public void hirePlayer(Player player) {
        team.addPlayer(player);
    }

    public void firePlayer(int jerseyNo) {
        team.lookupPlayer(jerseyNo)
                .ifPresent(player -> team.remove(player));
    }
}

答案 2 :(得分:0)

如果任何代码用于封装,那么该代码是正确的。封装的目的是提供一个从其他类隐藏变量的过程(即通过将变量设置为私有),并为其他类提供访问和修改变量的方法。这两个代码都正确地用于此目的。

如果您将“calculateSquare(int value)”方法用作“公共”,那么就会出现问题。其他类可以直接调用此方法而无需使用set / get方法。因此,只要你的这个方法是“私人的”,我认为两个代码都是正确的。