首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【抽象那些事】不必要的抽象

【抽象那些事】不必要的抽象

原创
作者头像
撸码那些事
发布于 2018-05-27 01:35:01
发布于 2018-05-27 01:35:01
30900
代码可运行
举报
文章被收录于专栏:撸码那些事撸码那些事
运行总次数:0
代码可运行

抽象型坏味

不必要的抽象

软件设计中引入实际上不需要的抽象时,将导致这种坏味。

##为什么不可以有不必要的抽象?

抽象实体应该具有单一而重要的职责。如果创建的没必要或是只是为了方便,它们承担的职责微不足道,甚至没有承担任何职责,这违反了抽象原则。

##不必要的抽象的潜在原因

使用的是面向对象语言,思维却是过程型编程思维

过程型思维常常会创建执行功能而不是表示事物的类。这种类通常只有一两个方法,而这些方法操作的数据位于独立地“数据类”中。

使用不合适的语言功能

例如,使用"常量类"而不是枚举。这增加了不必要的类。

过度设计

例如,为了表示与Customer对象相关联的客户ID,创建一个名为CustomerID的类。更好的设计是:在Customer对象中使用一个字符串来存储客户ID。

示例分析一

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class FormattableFlags
{
    /// <summary>
    /// 禁止显式实例化这个类
    /// </summary>
    private FormattableFlags()
    {
    }
    /// <summary>
    /// 将输出左对齐
    /// </summary>
    public  const int LEFT_JUSTIFY = 1;
    /// <summary>
    /// 将输出转换为大写
    /// </summary>
    public const int UPPERCASE = 2;
    /// <summary>
    /// 要求输出使用替换类型
    /// </summary>
    public const int ALTERNATE = 3;
}

我们上面分析过,使用"常量类"而不是枚举,增加了不必要的类。可以使用枚举替换掉"常量类",消灭掉不必要的类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public enum FormattableFlagsEnum
{
    /// <summary>
    /// 将输出左对齐
    /// </summary>
    LEFT_JUSTIFY = 1,
    /// <summary>
    /// 将输出转换为大写
    /// </summary>
    UPPERCASE = 2,
    /// <summary>
    /// 要求输出使用替换类型
    /// </summary>
    ALTERNATE = 3
}

示例分析二

一个电子商务应用程序,其中包含两个类:BestSellerBook和Book。每当客户程序要创建畅销书时,都创建一个BestSellerBook实例。在BestSellerBook内部只是将所有方法都委托给Book类,别的什么都不做。显然抽象BestSellerBook是多余的,因为其行为和抽象Book完全相同。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 图书类
/// </summary>
public class Book
{
    /// <summary>
    /// 价格
    /// </summary>
    public decimal Price { get; private set; }
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="price"></param>
    public Book(decimal price)
    {
        this.Price = price;
    }
    /// <summary>
    /// 修改图书价格
    /// </summary>
    /// <param name="price">图书价格</param>
    public void ModifyPrice(decimal price)
    {
        this.Price = price;
    }
}
/// <summary>
/// 畅销图书类
/// </summary>
public  class BestSellerBook
{
    private Book book = null;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="price"></param>
    public BestSellerBook(decimal price)
    {
        book = new Book(price);
    }
    /// <summary>
    /// 修改图书价格
    /// </summary>
    /// <param name="price">图书价格</param>
    public void ModifyPrice(decimal price)
    {
        book.ModifyPrice(price);
    }
}

对于功能有限,不值得创建的类,应将其删除。

我们可以将BestSellerBook类删除,并且在Book类中添加属性IsBestSeller。这样在客户程序创建Book实例时需要指出图书是否为畅销书,可设置属性IsBestSeller,而不用像以前那样创建BestSellerBook类的实例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 图书类
/// </summary>
public class Book
{
    /// <summary>
    /// 价格
    /// </summary>
    public decimal Price { get; private set; }
    /// <summary>
    /// 是否畅销书
    /// </summary>
    public bool IsBestSeller { get; private set; }
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="price">价格</param>
    /// <param name="isBestSeller">是否畅销书</param>
    public Book(decimal price, bool isBestSeller)
    {
        this.Price = price;
        this.IsBestSeller = isBestSeller;
    }
    /// <summary>
    /// 修改图书价格
    /// </summary>
    /// <param name="price">图书价格</param>
    public void ModifyPrice(decimal price)
    {
        this.Price = price;
    }
    /// <summary>
    ///  修改是否畅销书
    /// </summary>
    /// <param name="isBestSeller">是否畅销书</param>
    public void ModifyIsBestSeller(bool isBestSeller)
    {
        this.IsBestSeller = isBestSeller;
    }
}

现实考虑

###设计模式中的委托抽象

有些设计模式(如代理模式、门面模式和适配器模式)使用了委托,其中包含了一个看似不必要的类。例如,在对象适配器模式中,Adapter类看似只是将客户端请求委托给Adaptee类的相应方法。但是,Adapter类承担了明确而具体的职责:调整Adaptee类的接口,以满足客户端的需求。所以,判断抽象是否多余,还要具体情况具体分析。

对象适配器模式UML图

总结:

  1. 包含多余的抽象会增加设计的复杂性,影响整个设计的可理解性。
  2. 职责独特而明确的抽象有可能得到重用,而当抽象不承担任何职责或承担的职责微不足道时,就不太可能在其它地方重用。 ​

参考:《软件设计重构》


作者:撸码那些事

声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,不妨关注一下下方的【微信公众号】按钮,谢谢支持。转载与引用请注明出处。

微信公众号:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
最佳编码实践——单一职责原则
同时应用这些最佳实践,可以提升代码适应变更的能力。但是凡事要有度,过度使用虽然可以让代码有很高的自适应能力,但是会导致层次粒度过小而难以理解或使用,还会影响代码的可读性。
撸码那些事
2018/10/08
7450
最佳编码实践——单一职责原则
基于控制台的购书系统(Java 语言实现)
伴随互联网的蓬勃发展,网络购书系统作为电子商务的一种形式, 正以其高效、低成本的优势逐步成为新兴的经营模式,互联网的用途也不再局限于信息的浏览和发布,人们能够充分享受互联网带来的更多便利。网络购书系统正是适应了当今社会快节奏的生活,使顾客足不出户便可以方便、快捷、轻松地选购自己喜欢的图书。
爱敲代码的小杨.
2024/05/07
2560
基于控制台的购书系统(Java 语言实现)
设计模式-委托模式
在常用的23种设计模式中其实面没有委派模式(delegate)的影子,但是在 Spring 中委派模式确实用的比较多的一种模式,Spring MVC 框架中的DispatcherServlet其实就用到了委派模式
码哥字节
2020/03/24
1.7K0
设计模式-委托模式
Java二十三种设计模式-适配器模式(6/23)
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间可以一起工作,通过将一个类的接口转换成客户端期望的另一个接口。
正在走向自律
2024/12/18
2000
Java二十三种设计模式-适配器模式(6/23)
二、适配器模式
适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。
Yuyy
2022/09/21
3210
二、适配器模式
无处不在的适配器模式
对于适配器相信不会陌生,生活中的例子比比皆是,像耳机转接线,充电器适配器,水管适配接口等等。通过类比很容易理解软件中的适配器模式。
BUG弄潮儿
2021/01/05
6190
设计模式——职责链模式
设计模式——职责链模式
Java架构师必看
2021/05/14
3220
设计模式——职责链模式
设计模式——一文即可
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式,通过将对象的创建逻辑封装在一个工厂类中,客户端不需要知道具体的创建细节,只需要向工厂类请求所需对象即可。
学编程的小程
2024/01/17
7030
设计模式——一文即可
java实现简单的图书管理系统「建议收藏」
Book类: 定义了书的一些属性(书名,作者,价格,分类,状态)并且写了属性的get、set方法 Library类: 写了登录函数(1、普通用户,2、管理员,3、退出系统), Person类:定义的人的属性(姓名,性别,年龄)并且写了属性的get、set方法,定义了一些书。 operate接口:操作的接口,操作方法:查询/查阅,删除/借阅,增加/还书,显示书籍列表 Root类:继承了Person类,实现operate(查阅,删除,增加,显示书籍列表)接口 User类:继承了Person类,实现operate(查询,借阅,还书,显示书籍列表)接口 Main类:Main中的主函数开启整个项目。
全栈程序员站长
2022/09/14
1.3K0
java实现简单的图书管理系统「建议收藏」
设计模式第五讲-适配器模式
存在两种适配器: “对象适配器” 和 “类”适配器 (因为大部分语言不支持多重继承,所以此处指的是对象适配器)
用户2825413
2019/07/16
3730
设计模式第五讲-适配器模式
Java设计模式:适配器模式的三种形式(五)
适配器模式(Adapter Pattern)的核心概念是将一个类的接口转换成客户期望的另一个接口,从而让原本接口不兼容的类能够合作无间。适配器模式通过引入一个适配器类来实现这种转换,适配器类通常持有源类(即被适配的类)的引用,并实现目标接口(即客户期望的接口)。客户端代码通过调用适配器类的方法来间接调用源类的方法,从而实现接口的适配。
公众号:码到三十五
2024/03/19
3770
Java设计模式:适配器模式的三种形式(五)
用最简单的例子说明设计模式(三)之责任链、建造者、适配器、代理模式、享元模式
责任链模式 一个请求有多个对象来处理,这些对象是一条链,但具体由哪个对象来处理,根据条件判断来确定,如果不能处理会传递给该链中的下一个对象,直到有对象处理它为止 使用场景 1)有多个对象可以处理同一个
六月的雨
2018/05/14
6160
适配器模式与桥接模式:一分钟浅谈
在面向对象设计模式中,适配器模式和桥接模式都是非常重要的模式,它们帮助我们解决了一些常见的设计问题。本文将从概念、应用场景、实现方式以及常见问题等方面,对这两种模式进行简要介绍,并通过C#代码示例来加深理解。
Jimaks
2024/10/26
2770
一起学习设计模式--07.适配器模式
有的笔记本电脑工作电压是20V,但是国家标准用电电压是220V,如何让20V的笔记本电脑能够在220V的电压下工作?答案是引入一个电源适配器,俗称充电器/变压器,有了这个电源适配器,生活用电和笔记本电脑即可兼容。
独立观察员
2022/12/06
2680
一起学习设计模式--07.适配器模式
适配器模式(Adapter)
1.模式的定义: 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原来由于接口不兼容而不能一起工作的那些类可以一起工作。 适配器模式的分类: 通过继承实现Adapter (继承Adaptee
Java高级架构
2018/07/20
3400
使用c#,运用面向对象的思想构建的一个图书管理系统(无数据库)
使用c#,运用面向对象的思想构建的一个图书管理系统(无数据库) 实现截图如下图: 面向对象的思想,实现代码: namespace s1025_面向对象_ { //使用面向对象的思想构建图书管
戈贝尔光和热
2018/12/27
1.2K0
火车残骸和基本类型偏执问题解决方案
坏味道:缺乏封装。封装,将碎片式代码封装成可复用模块。但不同级别程序员对封装理解程度差异大,往往写代码的人认为自己提供了封装,但实际上,我们还是看到许多的代码散落在那里。
JavaEdge
2023/02/13
4280
设计模式-适配器模式
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
用户9322266
2022/10/09
2680
设计模式之适配器模式
本文通过老王使用纸质书籍阅读小王使用电子书籍的故事,详细说明设计模式中的结构型设计模式之适配器模式,分别对对象适配器和类适配器代码实现,最后为了加深理解,会列举适配器设计模式在JDK和Spring源码中的应用。
程序员田同学
2022/08/04
3820
设计模式实战-适配器模式,承上启下
适配器,其实很好理解,生活中也随处可见,比如电源适配器、usb 适配器等等,那么适配器模式,也被称为Wrapper 模式。
架构师修炼
2020/07/20
5170
设计模式实战-适配器模式,承上启下
相关推荐
最佳编码实践——单一职责原则
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档