CH07_封装

2023-11-15

封装记录(Encapsulate Record | 162)

曾用名:以数据类代替记录(Replace Record with Data Class)

在这里插入图片描述

organization = {name: "Acme Gooseberries", country: "GB"};
class Organization {
    constructor(data) {
        this._name = data.name;
        this._country = data.country;
	}
    get name() {return this._name;}
    set name(arg) {this._name = arg;}
    get country() {return this._country;}
    set country(arg) {this._country = arg;}
}

动机

记录型结构是多数编程语言提供的一种常见特性。它们能直观的组织起存在关联的数据,可以将数据作为有意义的端元传递,而不仅是一堆数据的拼凑。但简单的记录型结构也有缺陷,需要清晰的区分“记录中存储的数据”和“通过计算得到的数据”。

对于可变数据,建议使用类对象,对象可以隐藏结构的细节,该对象的用户不必追究存储的细节和计算的过程,同时,这种封装还有助于字段的改名(外界通过访问函数来获取字段的值)。

记录型结构可以有两种类型:一种需要声明合法的字段名字,另一种可以随便用任何字段名字。后者常由语言库本身实现,并通过类的形式提供出来,这些类称为散列(hash)、映射(map)、散列映射(hashmap)、字典(dictionary)或关联数组(associative array)等。但使用这类结构也有缺陷,那就是一条记录上持有什么字段往往不够直观。

程序中间常常需要互相传递嵌套的列表(list)或散列映射结构,这些数据结构后续经常需要被序列化成JSON或XML。这样的嵌套结构同样值得封装,这样,如果后续其结构需要变更或者需要修改记录内的值,封装能更好地应对变化。

做法

  • 对持有记录的变量使用封装变量(132),将其封装到一个函数中。

  • 创建一个类,将记录包装起来,并将记录变量的值替换为该类的一个实例。然后在类上定义一个访问函数,用于返回原始的记录。修改封装变量的函数,令其使用这个访问函数。

  • 测试

  • 新建一个函数,让它返回该类的对象,而非那条元素的记录。

  • 对于该记录的每处使用点,将原先返回记录的函数调用替换为那个返回实例对象的函数调用。使用对象上的访问函数来获取数据的字段,如果该字段的访问函数还不存在,那就创建一个。每次更改之后运行测试。

    如果该记录比较复杂,例如是个嵌套解构,那么先重点关注客户端对数据的更新操作,对于读取操作可以考虑返回一个数据副本或只读的数据代理。

  • 移除类对元素记录的访问函数,那个容易搜索的返回原始数据的函数也要一并删除。

  • 测试

  • 如果记录中的字段本身也是复杂结构,考虑对其再次应用封装记录(162)或封装集合(170)手法。

封装集合(Encapsulate Collection | 170)

在这里插入图片描述

class Person {
    get courses(){return this._courses;}
    set courses(aList){this._courses-aList;}
    // ...
}
class Person {
    get courses(){return this.courses.slice();}
    addCourse(aCourse){...}
    removeCourse(aCourse){...}
    // ...
}

动机

封装程序中的所有可变数据,可以很容易看清楚数据被修改的地点和修改方式,需要更改数据结构时就非常方便。但封装集合时通常会犯一个错误:只对集合变量的访问进行了封装,但依然让取值函数返回集合本身。这使得集合的成员变量可以直接被修改,而封装它的类则全然不知,无法介入。为避免此种情况,可以在类上提供一些修改集合的方法——通常是“添加”和“移除”方法。

一种避免直接修改集合的方法是,永远不直接返回集合的值。这种方法提倡,不要直接使用集合的字段,而是通过定义类上的方法来代替。

还有一种方法是,以某种形式限制集合的访问权,只允许对集合进行读操作(比如,在Java中可以很容易地返回集合的一个只读代理)。

最常见的做法是,为集合提供一个取值函数,但令其返回一个集合的副本。这样即使有人修改了副本,被封装的集合也不会受到影响。

采用哪种方法并无定式,最重要的是在同个代码库中做法要保持一致。

做法

  • 如果集合的引用尚未被封装起来,先用封装变量(132)封装它
  • 在类上添加用于“添加集合元素”和“移除集合元素”的函数(如果存在对集合的设值函数,尽可能先用移除设值函数(331)移除它。如果不能移除该设值函数,至少让它返回集合的一个副本)
  • 执行静态检查
  • 查找集合的引用点。如果有调用者直接修改集合,令该处调用使用新的添加/移除元素的函数。每次修改后执行测试
  • 修改集合的取值函数,使其返回一份只读数据,可以使用只读代理或者数据副本。
  • 测试

以对象取代基本类型(Replace Primitive with Object | 174)

曾用名:以对象取代数据值(Replace Data Value with Object)

曾用名:以类取代类型码(Replace Type Code with Class)

在这里插入图片描述

orders.filter(o => "high" === o.priority || "rush" === o.priority);
orders.filter(o => o.priority.higherThan(new Priority("normal")))

动机

开发初期,往往决定以简单的数据项表示简单的情况,比如使用数字或字符串等。但随着开发的进行,可能会发现这些简单数据不再那么简单。

一旦发现随某个数据的操作不仅仅局限于打印时,可以为它创建一个新类。一开始这个类也许只是简单包装一下简单类型的数据,不过只要有了类,日后添加业务累哦及就简单多了。

做法

  • 如果变量尚未被封装起来,先使用封装变量(132)封装它。
  • 为这个数据值创建一个简单的类。类的构造函数应该保存这个数据值,并为它提供一个取值函数。
  • 执行静态检查。
  • 修改第一步得到的设值函数,令其创建一个新类的对象并将其存入字段,如果有必要的话,同时修改字段的类型声明
  • 修改取值函数,令其调用新类的取值函数,并返回结果。
  • 测试
  • 考虑对第一步得到的访问函数使用函数改名(124),以便更好反应其用途。
  • 考虑应用将引用对象改为值对象(252)或将值对象改为引用对象(256),明确指出先对象的角色是值对象还是引用对象。

以查询取代临时变量(Replace Temp with Query | 178)

在这里插入图片描述

const basePrice = this._quantity * this._itemPrice;
if (basePrice > 1000)
	return basePrice * 0.95;
else
	return basePrice * 0.98;
get basePrice() {this._quantity * this._itemPrice;}
...
if (this.basePrice() > 1000)
	return this.basePrice() * 0.95;
else
	return this.basePrice() * 0.98;

动机

临时变量的一个作用是保存某段代码的返回值,以便在函数的后面部分使用它。临时变量允许编程中引用之前的值,既能解释它的含义,还能避免对代码进行重复计算。但尽管使用变量很方便,很多时候还是值得更进一步,将它们抽取成函数。

抽取成函数能避免在多个函数中重复编写计算逻辑。

这项重构手法在类中施展效果最好,因为类为待提炼函数提供了一个共同的上下文。

以查询取代临时变量(178)手法只适用于处理某些类型的临时变量:那些只被计算一次且之后不再被修改的变量。

做法

  • 检查变量在使用前是否已经完全计算完毕,检查计算它的那段代码是否每次都能得到一样的值
  • 如果变量目前不是只读的,但是可以改造成为只读变量,那就先改造它。
  • 测试
  • 将为变量赋值的代码提炼成函数(确保待提炼函数没有副作用。若有,先应用将查询函数和修改函数分离(306)手法隔离副作用)
  • 测试
  • 应用内联变量(123)手法移除临时变量

提炼类(Extract Class |182 )

反向重构:内联类(186)

在这里插入图片描述

class Person {
    get officeAreaCode() {return this._officeAreaCode;}
    get officeNumber() {return this._officeNumber;}
}
class Person {
    get officeAreaCode() {return this._telephoneNumber.areaCode;}
    get officeNumber() {return this._telephoneNumber.number;}
}
class TelephoneNumber {
    get areaCode() {return this._areaCode;}
    get number() {return this._number;}
}

动机

你也许听过类似这样的建议:一个类应该是一个清晰的抽象,只处理一些明确的责任,等等。但是在实际工作中,类会不断成长扩展。你会在这儿加入一些功能,在那儿加入一些数据。给某个类添加一项新责任时,你会觉得不值得为这项责任分离出一个独立的类。于是,随着责任不断增加,这个类会变得过分复杂。很快,你的类就会变成一团乱麻。

维护一个有大量函数和数据的类,这样的类往往因为太大而不易理解。此时需要考虑哪些部分可以分离出去,并将它们分离到一个独立的类中。如果某些数据和某些函数总是一起出现,某些数据经常同时变化甚至彼此相依,这就表示应该将它们分离出去。

往往在开发后期出现的信号是类的子类化方式。如果发现子类化只影响类的部分特性,或如果发现某些特性需要以一种方式来子类化,某些特性则需要以另一种方式子类化,这就意味着需要分解原来的类。

做法

  • 决定如何分解类所负的责任。
  • 创建一个新的类,用以表现从旧类中分离出来的责任(如果旧类剩下来的责任与旧类的名称不符合,为旧类改名)
  • 构造旧类时创建一个新类的实列,建立“从旧类访问新类”的连接关系
  • 对于想搬移的每一个字段,运用搬移字段(207)搬移之。每次更改后测试。
  • 使用搬移函数(198)将必要函数搬移到新类。先搬移较低层的函数(也就是“被其他函数调用”多于“调用其他函数”者)。每次更改后运行测试
  • 检查两个类的接口,去掉不再需要的函数,必要时为函数重新取一个合适新环境的名字
  • 决定是否公开新的类。如果确定需要,考虑新类应用将引用对象改为值对象(252)使其成为一个值对象

内联类(Inline Class | 186)

反向重构:提炼类(182)

在这里插入图片描述

class Person {
    get officeAreaCode() {return this._telephoneNumber.areaCode;}
    get officeNumber() {return this._telephoneNumber.number;}
}
class TelephoneNumber {
    get areaCode() {return this._areaCode;}
    get number() {return this._number;}
}
class Person {
    get officeAreaCode() {return this._officeAreaCode;}
    get officeNumber() {return this._officeNumber;}
}

动机

内联类正好与提炼类(182)相反。如果一个类不再承担足够责任,不再有单独存在的理由(这通常是因为此前的重构动作移走了这个类的责任),将这个类塞进另外一个类中。

应用这个手法的另一个场景是,手头有两个类,想重新安排它们肩负的职责,并让它们产生关联。这时发现先用本手法将它们内联成一个类再用提炼类(182)去分离其职责会更加简单。

做法

  • 对于待内联类(源类)中的所有public函数,在目标类上创建一个对应的函,新创建的所有函数应该直接委托至源类。
  • 修改袁磊public方法的所有引用点,令它们调用目标类对应的委托方法。每次更改后运行测试。
  • 将源类中的函数于数据全部搬移到目标类,每次修改之后进行测试,直到源类编程空壳为止。
  • 删除源类

隐藏委托关系(Hide Delegate | 189)

反向重构:移除中间人(192)

在这里插入图片描述

manager = aPerson.department.manager;
manager = aPerson.manager;
class Person {
	get manager() {return this.department.manager;}
}

动机

一个好的模块化的设计,“封装”是最关键特征之一。“封装”意味着每个模块都应该尽可能少了解系统的其他部分。一旦发生变化,需要了解这一变化的模块就会比较少,这会使变化比较容易进行。

如果某些客户端先通过服务对象的字段得到另一个对象(受托类),然后调用后者的函数,那么客户就必须知晓这一层委托关系。万一受托类修改了接口,变化会波及通过服务对象使用它的所有客户端。

做法

  • 对于每个委托关系中的函数,在服务对象端建立一个简单的委托函数。
  • 调整客户端,令它只调用服务对象提供的函数。每次调整后运行测试。
  • 如果将来不再有任何客户端需要取用Delegate(受托类),便可移除服务对象中的相关函数。
  • 测试。

移除中间人(Remove Middle Man | 192)

反向重构:隐藏委托关系(189)

在这里插入图片描述

manager = aPerson.manager;
class Person {
	get manager() {return this.department.manager;}
}
manager = aPerson.department.manager;

动机

“封装受托对象”这层封装也是有代价的。每当客户端要使用受托类的新特性时,你就必须在服务端添加一个简单委托函数。随着受托类的特性(功能)越来越多,更多的转发函数就会使人烦躁。服务类完全变成了一个中间人(81),此时就应该让客户直接调用受托类。

很难说什么程度的隐藏才是合适的,可以在系统运行过程中不断进行调整。

做法

  • 为受托对象创建一个取值函数。

  • 对于每个委托函数,让其客户端转为连续的访问函数调用。每次替换后运行测试

    替换完委托方法的所有调用点后,就可以删掉这个委托方法了。
    这能通过可自动化的重构手法来完成,可以先对受托字段使用封装变量(132),再应用内联函数(115)内联所有使用它的函数。

替换算法(Substitute Algorithm | 195)

在这里插入图片描述

function foundPerson(people) {
    for(let i = 0; i < people.length; i++) {
        if (people[i] === "Don") {
        	return "Don";
        }
        if (people[i] === "John") {
        	return "John";
        }
        if (people[i] === "Kent") {
        	return "Kent";
        }
    }
    return "";
}
function foundPerson(people) {
    const candidates = ["Don", "John", "Kent"];
    return people.find(p => candidates.includes(p)) || '';
}

动机

如果发现做一件事可以有更清晰的方式,就用比较清晰的方式取代复杂的方式。“重构”可以把一些复杂的东西分解为较简单的小块。

替换一个巨大且复杂的算法是非常困难的,只有先将它分解为较简单的小型函数,才能很有把握地进行算法替换工作。

做法

  • 整理一下待替换的算法,保证它已经被抽取到一个独立的函数中。
  • 先只为这个函数准备测试,以便固定它的行为。
  • 准备好另一个(替换用)算法。
  • 执行静态检查。
  • 运行测试,比对新旧算法的运行结果。如果测试通过,那就大功告成;否则,在后续测试和调试过程中,以旧算法为比较参照标准。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CH07_封装 的相关文章

  • 项目中的if else太多了,该怎么重构?

    if else 太多了 最近跟着公司的大佬开发了一款IM系统 类似QQ和微信哈 就是聊天软件 我们有一部分业务逻辑是这样的 if msgType 文本 dosomething else if msgType 图片 doshomething
  • Java实体类中封装其他实体类并引用

    在Java开发过程中有很多情况是二个表及以上的联合操作 这是需要分清楚表的主次关系 在引用的时候有人会把二个表的数据全都封装在一个实体类中 然后在对这个实体类进行操作 但如果是三个表呢 四个表呢 还都封装在一个实体类吗 这样被封装的实体类的
  • Photoscan/Metashape 2.0.0中的地面激光扫描处理

    在Metashape 原Photoscan 2 0 0 结构化地面激光扫描和非结构化航空激光扫描都可以使用导入点云 文件 gt 导入 gt 导入点云 命令导入 导入时会保留所有点属性 包括结构化信息 本文讨论以下主题 如何将激光扫描数据导入
  • vue-chat项目之重构与体验优化

    前言 vue chat 也就是我的几个月之前写的一个基于vue的实时聊天项目 到目前为止已经快满400star了 注册量也已经超过了1700 消息量达2000 由于一直在实习 没有时间对它频繁地更新 趁着这个国庆 对他进行了一次重构 希望我
  • PHP自己的框架2.0版本目录结构和命名空间自动加载类(重构篇一)

    目录 1 目录结构演示效果 2 搭建目录结构 以及入口public gt index php 3 引入core下面core gt base php 4 自动加载实现core gt fm gt autoload php 5 框架运行文件cor
  • JS 函数

    JS 函数 关键字形式的函数
  • Vue + axios + vant 封装公共请求

    import axios from axios 引入axios import router from router 引入路由 import Toast from vant 引入提示层 export function post obj 调用时
  • LQFP100封装尺寸图

    下图是LQFP100封装规格的参数 根据下图的参数 即可制作LQFP100的PCB封装
  • 排阻的封装尺寸

    http arlen opcom blog 163 com blog static 33775037201011144225518 0402封装 0603封装 1 电阻封装尺寸与功率关系 通常来说 0201 1 20W 0402 1 16W
  • CH07_封装

    封装记录 Encapsulate Record 162 曾用名 以数据类代替记录 Replace Record with Data Class organization name Acme Gooseberries country GB c
  • 2021-02-28

    simulink控制器封装库 控制器封装库 一 封装库的安装和LADRC模块的使用
  • 【配电网故障重构SOP】基于二阶锥松弛的加光伏风机储能进行的配电网故障处理和重构【考虑最优潮流】(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 电力系统最优潮流 综合考虑电网安全性和经济
  • VPP plugin so 的封装与解耦

    封装与解耦 每一个 plugin 封装了一个独立的功能模块 模块依赖的外部 so 接口也封装在每个 plugin 中 vpp 提供统一的使能 禁能 初始化 plugin 的框架 同时每个 plugin 对外提供的接口也使用统一的方式 大多采
  • AndroidStudio将module变为library

    文章注明出处可随意转载 请尊重别人的劳动成果 前言 在一个application当中 可能会存在多个module 有时也会有一个module包含其他module的需求 在完成这个需求时 Google了很多 全是2014年之前的一些老文章 现
  • 基于Nonconvex规划的配电网重构研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 本文基于Nonconvex规划的配电网重构
  • 【Node.js实战】一文带你开发博客项目之Koa2重构(实现session、开发路由、联调、日志)

    个人简介 个人主页 前端杂货铺 学习方向 主攻前端方向 也会涉及到服务端 个人状态 在校大学生一枚 已拿多个前端 offer 秋招 未来打算 为中国的工业软件事业效力n年 推荐学习 前端面试宝典 Vue2 Vue3 Vue2 Vue3项目实
  • 腾讯mini项目-【指标监控服务重构】2023-07-30

    今日已办 调研 CPU Memory Cadivisor adivisor gt Prometheus gt Grafana SigNoz Web google cadvisor Analyzes resource usage and pe
  • python自动化笔记(十一)——openpyxl之封装

    封装一个可以读取任意excel文件的方法 可以指定读取的表单 当我们多次从excel中读取数据时 就不用重复地写代码 只需调用封装的类即可 一 封装的excel类实现的需求是什么 1 读取表头数据 2 读取表头以外的所有数据 返回值 列表
  • 《重构的时机和方法》一本值得程序员都认真读的书

    写在前面 重构的时机和方法 是一本关于软件开发中重构技术的书籍 它以独特的风格和内容优势 为读者提供了全面而易于理解的指导 帮助他们在实际项目中应用重构技术 提高代码质量和开发效率 这本书由两个不同风格的部分组成 旨在满足不同读者群体的需求
  • 目标检测算法改进系列之添加SCConv空间和通道重构卷积

    SCConv 空间和通道重构卷积 SCConv 空间和通道重构卷积 的高效卷积模块 以减少卷积神经网络 CNN 中的空间和通道冗余 SCConv旨在通过优化特征提取过程 减少计算资源消耗并提高网络性能 该模块包括两个单元 1 空间重构单元

随机推荐

  • webpack5 基础配置(7) eslint-ts的使用和在webpack中的配置 加载vue文件

    上一节最后讲到了typescript的使用 ts loader本质上也是利用与tsc 所以安装ts loader的同时需要安装typescript 如果你没安装typescript 在你安装ts loader的时候 会根据ts loader
  • Git回退分支到某版本

    1 找到要回退的版本号 TortoiseGit gt Show Log gt 选中要回退的版本 gt Reset xxx to this gt commit旁复制版本号 2 本地回退到某版本 git reset hard 9224ad994
  • Idea 中 Docker 的 log 乱码

    IDEA 本地调试代码中英文显示全部正常 但是用 idea 中的 docker 插件部署到服务器 再查看 容器中 log 的时候 log 里面的中文全是乱码 就开启了一段排查过程 开始一个个排查 程序员的日常 查看容器中语言 字符集 环境
  • 【Android】java.lang.UnsatisfiedLinkError: No implementation found for void xx xx xx -- 问题解决

    Android在开发过程中总会出现各种各样的bug 要想畅通无阻的完成一个app的 开发工作固然有点难度 这不 我的程序跑着跑着又报错了 AndroidRuntime FATAL EXCEPTION Thread 4903 Process
  • 反汇编二进制代码

    最近又做了一些内核hook的工作 繁琐的地方在于二进制指令的可读性上 下面简要记录dump出指令二进制 之后利用binutils来转成可读的汇编代码 hook的主要流程参考之前的linux内核态hook模块 splice 主要就是构建一个t
  • 理解MySQL回表

    回表就是先通过数据库索引扫描出数据所在的行 再通过行主键id取出索引中未提供的数据 即基于非主键索引的查询需要多扫描一棵索引树 因此 可以通过索引先查询出id字段 再通过主键id字段 查询行中的字段数据 即通过再次查询提供MySQL查询速度
  • Java编程——输出1000以内的素数(质数)

    素数的定义是什么 质数 prime number 又称素数 有无限个 一个大于1的自然数 除了1和它本身外 不能被其他自然数 质数 整除 换句话说就是该数除了1和它本身以外不再有其他的因数 否则称为合数 public class demo7
  • STM32库函数TIM_SetCompare()的工作机制测试

    一 TIM SetCompare 函数的定义 其中 TIM SetCompareX 这个函数有四个 它们为 TIM SetCompare1 TIM SetCompare2 TIM SetCompare3 TIM SetCompare4 同时
  • 引脚悬空是什么电平_STM32单片机必须掌握的八种IO口模式和引脚配置方式

    八种IO口模式 STM32有八种IO口模式 分别是 模拟输入 浮空输入 上拉输入 下拉输入 开漏输出 推挽输出 复用开漏输出和复用推挽输出 1 模拟输入 GPIO Mode AIN模拟输入 即关闭施密特触发器 将电压信号传送到片上外设模块
  • 关于STM32F103 TIM2重映射

    关于STM32F103 TIM2重映射 如何使用重映射和如何重映射为哪些管脚 这里不详细讲解 可以百度找到 下面讲的是tim2重映射为PA15 PB3 PB10 PB11 这里的设置网上也有讲解 但是如果功能较为复杂的程序 外设用的比较多的
  • 黑马JVM总结(五)

    1 方法区 它是所有java虚拟机 线程共享的区 存储着跟类的结构相关的信息 类的成员变量 方法数据 成员方法 构造器方法 特殊方法 类的构造器 方法区在虚拟机启动时被创建 方法区逻辑上是堆的组成部分 但是不同的JVM厂商实现是不一样的 O
  • 进程,线程,协程总结

    进程 三种状态 就绪态 运行的条件都已经慢去 正在等在cpu执行 执行态 cpu正在执行其功能 等待态 等待某些 条件满足 例如一个程序sleep了 此时就处于等待态 生命周期 用户编写代码 代码本身是以进程运行的 启动程序 进入进程 就绪
  • Eclipse搭建stm32+jlink开发环境全攻略

    Eclipse搭建stm32 jlink开发环境全攻略 初级篇 前言 为什么需要这样的开发环境 免费 跨平台 自由度高 Eclipse代码提示功能强大 MDK弱爆了 注 本人原创 转载注明作者 by 秋之前 email xia mengli
  • paddleOCR踩坑记

    paddleOCR踩坑记 训练莫名终止或者评估莫名终止 训练和评估的效果都特别好 预测的效果却特别差 使用gen label py报错 将paddleOCR转成pytorch框架 训练莫名终止或者评估莫名终止 这是因为在tools prog
  • 2023全国大学生数学建模竞赛B题思路模型代码

    目录 一 选题建议先发布 思路模型代码论文第一时间更新 获取见文末名片 二 选题建议 后续思路代码论文 B 题 多波束测线问题 各题分析 获取完整思路代码见此处名片 一 选题建议先发布 思路模型代码论文第一时间更新 获取见文末名片 二 选题
  • python使用matplotlib:subplot绘制多个子图

    CSDN GitHub python使用matplotlib subplot绘制多个子图 AderXCoding language python matplotlib subplot 转自 https blog csdn net gatie
  • 解决eclipse启动报错问题:Could not create the Java Virtual Machine...

    电脑重装系统后 下了很多软件 其中就包括eclipse 但是在安装好后打开eclipse时报错 点击确定出现下面这样 网上找了原因 是因为自己在安装JDK的时候在C windows System32文件中生成了三个文件java exe ja
  • vue.js 开发环境搭建最简单攻略

    更新 本篇文章是我很之前写的 对于一些没有工作 或者js基础不太好的同学 建议不必大费周章去搭建环境 可以直接引用 vue js 就可以进行学习 练习 有些过程是水到渠成的 当初很多不懂的东西慢慢就懂了 官方指南假设你已有HTML CSS和
  • 小学数学题的Java实现

    昨天 去朋友家一起做饭 刚好有小孩问我问题 说你不是学计算机的吗 那你教我做一道数学题 我刚开始看的时候愣了一下 不过 想了一会还是解决 题目是这样的 有一袋糖果 每次从袋子里面拿走一半又放进袋子一颗 经过5次操作后 袋子还剩余4颗糖果 请
  • CH07_封装

    封装记录 Encapsulate Record 162 曾用名 以数据类代替记录 Replace Record with Data Class organization name Acme Gooseberries country GB c