首页 > 其他分享 >3、错误的面向对象

3、错误的面向对象

时间:2023-06-24 16:45:29浏览次数:33  
标签:totalPrice 错误 items 面向对象 getter setter public 属性

1、滥用 getter、setter 方法

在之前参与的项目开发中,我经常看到有同事定义完类的属性之后,就顺手把这些属性的 getter、setter 方法都定义上
有些同事更加省事,直接用 IDE 或者 Lombok 插件(如果是 Java 项目的话)自动生成所有属性的 getter、setter 方法

当我问起,为什么要给每个属性都定义 getter、setter 方法的时候,他们的理由一般是
为了以后可能会用到,现在事先定义好,类用起来就更加方便,而且即便用不到这些 getter、setter 方法,定义上它们也无伤大雅

实际上这样的做法我是非常不推荐的
它违反了面向对象编程的封装特性,相当于将面向对象编程风格退化成了面向过程编程风格

在设计实现类的时候,除非真的需要,否则尽量不要给属性定义 setter 方法
除此之外,尽管 getter 方法相对 setter 方法要安全些,但是如果返回的是集合容器(比如例子中的 List 容器),也要防范集合内部数据被修改的危险

1.1、示例

public class ShoppingCart {

    private int itemsCount;
    private double totalPrice;
    private List<ShoppingCartItem> items = new ArrayList<>();

    public int getItemsCount() {
        return itemsCount;
    }

    public void setItemsCount(int itemsCount) {
        this.itemsCount = itemsCount;
    }

    public double getTotalPrice() {
        return totalPrice;
    }

    public void setTotalPrice(double totalPrice) {
        this.totalPrice = totalPrice;
    }

    public List<ShoppingCartItem> getItems() {
        return items;
    }

    public void addItem(ShoppingCartItem item) {
        items.add(item);
        itemsCount++;
        totalPrice += item.getPrice();
    }

    // ... 省略其他方法 ...
}

1.2、解释

在这段代码中,ShoppingCart 是一个简化后的购物车类,有三个私有(private)属性:itemsCount、totalPrice、items

  • 对于 itemsCount、totalPrice 两个属性,我们定义了它们的 getter、setter 方法
  • 对于 items 属性,我们定义了它的 getter 方法和 addItem() 方法

代码很简单,理解起来不难,那你有没有发现,这段代码有什么问题呢?

  • 我们先来看前两个属性:itemsCount 和 totalPrice
    虽然我们将它们定义成 private 私有属性,但是提供了 public 的 getter、setter 方法,这就跟将这两个属性定义为 public 公有属性,没有什么两样了
    外部可以通过 setter 方法随意地修改这两个属性的值
    除此之外,任何代码都可以随意调用 setter 方法,来重新设置 itemsCount、totalPrice 属性的值,这也会导致其跟 items 属性的值不一致
    而面向对象封装的定义是:通过访问权限控制,隐藏内部数据,外部仅能通过类提供的有限的接口访问、修改内部数据
    所以暴露不应该暴露的 setter 方法,明显违反了面向对象的封装特性
    数据没有访问权限控制,任何代码都可以随意修改它,代码就退化成了面向过程编程风格的了
  • 我们再来看 items 这个属性,对于 items 这个属性,我们定义了它的 getter 方法和 addItem() 方法,并没有定义它的 setter 方法
    这样的设计貌似看起来没有什么问题,但实际上并不是
    对于 itemsCount 和 totalPrice 这两个属性来说,定义一个 public 的 getter 方法,确实无伤大雅,毕竟 getter 方法不会修改数据
    但是对于 items 属性就不一样了,这是因为 items 属性的 getter 方法,返回的是一个 List集合容器
    外部调用者在拿到这个容器之后,是可以操作容器内部数据的,也就是说,外部代码还是能修改 items 中的数据,比如像下面这样
ShoppingCart cart = new ShoppCart();
// ...
cart.getItems().clear(); // 清空购物车

你可能会说,清空购物车这样的功能需求看起来合情合理啊,上面的代码没有什么不妥啊
你说得没错,需求是合理的,但是这样的代码写法,会导致 itemsCount、totalPrice、items 三者数据不一致,我们不应该将清空购物车的业务逻辑暴露给上层代码
正确的做法应该是:在 ShoppingCart 类中定义一个 clear() 方法,将清空购物车的业务逻辑封装在里面,透明地给调用者使用
ShoppingCart 类的 clear() 方法的具体代码实现如下

// 位于 ShoppingCart 类中
public void clear() {
    items.clear();
    itemsCount = 0;
    totalPrice = 0.0;
}

1.3、更多问题

你可能还会说,我有一个需求,需要查看购物车中都买了啥,那这个时候,ShoppingCart 类不得不提供 items 属性的 getter 方法了,那又该怎么办才好呢?
如果你熟悉 Java 语言,那解决这个问题的方法还是挺简单的
我们可以通过 Java 提供的 Collections.unmodifiableList() 方法,让 getter 方法返回一个不可被修改的 UnmodifiableList 集合容器
而这个容器类重写了 List 容器中跟修改数据相关的方法,比如 add()、clear() 等方法
一旦我们调用这些修改数据的方法,代码就会抛出 UnsupportedOperationException 异常,这样就避免了容器中的数据被修改,具体的代码实现如下所示

public class ShoppingCart {

    // ...  省略其他代码 ...

    public List<ShoppingCartItem> getItems() {
        return Collections.unmodifiableList(items);
    }
}
public class UnmodifiableList<E> extends UnmodifiableCollection<E> implements List<E> {

    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }

    public void clear() {
        throw new UnsupportedOperationException();
    }

    // ...  省略其他代码 ...
}
ShoppingCart cart = new ShoppingCart();
List<ShoppingCartItem> items = cart.getItems();
items.clear();// 抛出 UnsupportedOperationException 异常

不过,这样的实现思路还是有点问题
因为当调用者通过 ShoppingCart 的 getItems() 获取到 items 之后,虽然我们没法修改容器中的数据,但我们仍然可以修改容器中每个对象(ShoppingCartItem)的数据
听起来有点绕,看看下面这几行代码你就明白了

ShoppingCart cart = new ShoppingCart();
cart.add(new ShoppingCartItem(...));

List<ShoppingCartItem> items = cart.getItems();
ShoppingCartItem item = items.get(0);
item.setPrice(19.0); // 这里修改了 item 的价格属性

标签:totalPrice,错误,items,面向对象,getter,setter,public,属性
From: https://www.cnblogs.com/lidong422339/p/17501287.html

相关文章

  • Angular 应用里 NullInjectorError - No provider for XX 错误的一个场景和分析过程
    最近处理客户incident,有个客户从Spartacus4升级到5.2之后,启动Storefront,console遇到了一个错误消息:NullInjectorError-NoproviderforAnonymousConsentTemplatesAdapter!引起这个错误消息的场景有很多,这个incident最后的场景是:以前的module通过loadedforroot完......
  • JAVA面向对象程序设计_PTA题目集07-11总结分析
    JAVA面向对象程序设计_PTA题目集07-11总结分析前言:天将降大任于是人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行拂乱其所为。所以动心忍性,增益其所不能。随堂检测在最末浅析。 题目集七:知识点:类间设计,类的设计。题量:一题,菜单计价五。难度:八个满分。 题目集八:知识点:类......
  • rust 集合、错误处理、泛型、Trait、生命周期、包
    集合组织特性相同的数据;泛型可以定义任何抽象数据类型;生命周期限制所有权的作用域范围;错误处理使程序更健壮。集合一组特性相同的数据集合,除了基本数据类型的元组、数组。rust标准库提供了一些非常有用的数据结构。Vector存储列表通过类型Vec<T>定义。只能存储相同类型的值,......
  • “NTLDR is missing”是指在Windows启动过程中发生了一个错误,系统找不到NTLDR文件。NT
    NTLDR(NTLoader)是Windows操作系统早期版本(如WindowsNT和WindowsXP)使用的引导加载程序。随着后续Windows版本的发布,引导加载程序也发生了一些变化和更新。以下是各个版本中NTLDR的功能更新的简要说明:WindowsNT4.0:支持在启动时选择使用哪个操作系统。提供了基本的恢复控制台......
  • C++面向对象技术与C++课程设计任务书[2023-06-23]
    C++面向对象技术与C++课程设计任务书[2023-06-23]面向对象技术与C++课程设计任务书题目1 小型学籍管理系统班级 21060101~02 指导教师 耿军雪姓名 学号 地点 G1-203 完成时间 2023/6/262023/6/30【目的与要求】1、目的:(1)要求学生达到熟练掌握C++语言的基本知识和技能;(2)基......
  • 解决git出现fatal: detected dubious ownership in repository at XXXXX的错误
    在window环境下,使用git命令时报错fatal:detecteddubiousownershipinrepositoryatXXXXXX,图片如下解决方法如下添加一行代码gitconfig--global--addsafe.directory"*";......
  • UDP recvfrom error错误10022
    fromlen参数没有初始化from参数没有设置正确,也就是结构问题终于发现原来是bind函数的问题。由于在文件开头使用了usingnamespacestd导致默认的bind变成了functional中的那个,而不是socket的bind,导致绑定一直没有成功。当然,也可能是套接字端口被占用......
  • 记录一次 大意导致的错误(暂存)
    在部署到西藏职院测试流程时(Net6),有个在线预览功能,pc端可以,但是在微信端预览时就报错了,提示ERR_BLOCKED_BY_RESPONSE,百度设置x-frame-options=sameorigih,还是有问题,链接有一个http和一个https(问题就出在这里,微信端被强制输出为https导致),但是当时直接把流程界面通过h......
  • Java—面向对象
    概念:面向对象编程——以类的方式组织代码,以对象的组织数据。1.类和对象类是一个模板:抽象,对象:一个具体的实例。2.方法就是函数。3.对象的引用引用类型:基本类型(8)对象是通过引用来操作的:栈——>堆。4.属性在Java里叫字段。5.对象的创建和使用必须使用new关键字构建对象,构......
  • 打开PHP和Apache的错误提示
    如果使用PHP+Apache,在缺省设置下,PHP编码错误是不会提示的,这对于开发来说,是很不方便的。可以使用以下步骤打开出错提示:1.打开php.ini文件。以我的ubuntu为例,这个文件在:/etc/php5/apache2目录下。2.搜索并修改下行,把Off值改成Ondisplay_errors=Off3.搜索下行error_reporting......