如何写出优美的代码(二)

(本文思想基本来自于经典著作《重构》一书)

上一篇 http://www.cnblogs.com/ceys/archive/2012/03/05/2379842.html#commentform

 

 

上一篇文章主要讲了怎么给函数整容。现在我们大家基本上都使用面向对象语言,什么样的“对象”才是优美的呢?

类中的函数、字段应该和该类最紧密相关,如果和另一个类有更多交互,搬移它。搬移字段时,如果很多函数引用了该字段,可以把该字段用get/set方法自我封装起来。这样搬移时只需要修改get/set访问函数。

一个类应该是一个清楚的抽象,处理一些明确的责任。否则建立一个新类,用以表现从旧类中分离出来的责任。提炼类是改善并发程序的一种常用技术,因为提炼后可以给两个类分别加锁。需要注意,提炼出新类,要考虑要不要对用户公开这个新类。如果允许任何用户修改其对象的任何部分,它就成为引用对象;如果不允许任何人不通过对象A修改它的任何部分,则可以将其设为不可修改的,或为它提供不可修改的接口,将A中所有与其相关的函数委托之,从而完全隐藏这个类。

每个对象应该尽可能少了解系统的其它部分,当客户通过一个委托类调用另一个对象,在服务类上建立客户需要的所有函数,隐藏委托关系,减少耦合。然而,如果受托类的功能越来越多,服务类就完全变成了一个中介。很难说隐藏到什么程度是合适的。

当需要为服务类提供一些额外函数,但无法修改这个类时,建立一个新类,使它包含这些额外函数,成为源类的子类或包装类。如果函数较少,直接引入外加函数即可。

子类的工作量比较少,但它有两个问题:第一,必须在对象创建期实施,如果对象创建之后,就不能用了;第二,子类化会产生一个子类对象,如果有其它对象引用了旧对象,就同时又两个对象保存了原数据,如果原数据允许被修改,一个修改动作无法同时改变两份副本。这个时候就要用包装类。包装类需要为原始类的所有函数提供委托函数。 

 

二、重新组织数据

 

面向对象语言有一个有用的特征:允许定义新类型。这样我们可以利用对象,组织数据。

如果有一个数据项,需要与其他数据和行为一起使用才有意义,将数据项变为对象。

如果一个类衍生出许多彼此相等的实例,希望将它们替换为同一个对象,将这个值对象变成引用对象。每个引用对象都代表真实世界中的一个事物,可以以==检查两个对象是否相等。值对象由其所含数值定义,不在意副本存在。这里举个简单例子:

值对象:

class Customer {public Customer(String name) {_name = name;}public String getName() {return _name;}private final String _name;
}
class Order {public Order(String customerName) {_customer = new Customer(customerName);}//set,get...private Customer _customer;//other function use Customer...
}

这里,就算多分订单属于同一客户,每个Order对象还是拥有各自的Customer对象。现在把它改成引用对象,即每个客户名称只对应一个Customer对象:

class Customer {public static Customer getNamed (String name) {return (Customer) _instances.get(name);}private Customer (String name) {_name = name;}private static Dictionary _instances = new Hashtable();static void loadCustomers() {new Customer("Gang Li").store();}private void store() {_instances.put(this.getName(), this);}//...
}
class Order {public Order (String customer) {_customer = Customer.getNamed(customer);}//...
}	

我们首先创建工厂函数,以控制Customer对象的创建过程。然后,需要决定如何访问Customer对象,可以利用另一个对象访问,但这里Order类没有一个明显的字段可用于访问,所以可以创建一个对象保存所有Customer对象。这里为了简化,利用Customer里的静态字典保存。让Customer类作为访问点。然后,决定何时创建Customer对象。这里为了简化,在loadCustomer里预先把需要使用的Customer对象创建好。

引用对象可能造成内存区域之间错综复杂的关联。在分布系统和并发系统中,不可变的值对象特别有用,因为无需考虑他们的同步问题。这里的不可变指的是对象自身不能改变,比如money对象。当判断相等时需覆写equals()和hashCode()。(实现hashCode的简单办法是把equals()使用的所有字段安慰异或操作)。

如果两个类都需要使用对方特性,但只有一条单向连接,添加一个反向指针,并使修改函数能同时更新两条连接。以上面的代码为例:

class Customer {//客户拥有多份订单private Set _orders = new HashSet();//添加只包内可见的辅助函数,让Order可以直接访问_orders集合Set friendOrders() {return _orders;}//如果需要在Customer中也能修改连接,就让它调用控制函数void addOrder(Order arg) {arg.setCustomer(this);}
class Order...void setCustomer(Customer arg) {if (_customer != null)_customer.friendOrders().remove(this);_customer = arg;if (_customer != null)_customer.friendOrders().add(this);}	

如果一个数组,其元素各自代表不同的东西,以对象替换数组

以字面常量取代魔法数

如果有个函数返回一个集合,让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。如果函数返回集合自身,会让用户在拥有者不知情的情况下修改集合修改集合。所以不该为集合提供设置函数。但可以提供为集合增减元素的函数。例如:

class Person{//封装集合操作public void addCourse (Course arg) {_courses.add(arg);}public void removeCourse (Course arg) {_courses.remove(arg);}public void initializeCourses(Set arg) {Assert.isTrue(_courses.isEmpty());Iterator iter = arg.iterator();while (iter.hasNext()) {addCourse((Course) iter.next());}}//确保没有用户通过取值函数修改集合public Set getCourses() {return Collections.unmodifiableSet(_courses);}private Set _courses = new HashSet();
}

转载于:https://www.cnblogs.com/ceys/archive/2012/03/09/2388356.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377665.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

转:链表相交问题 详解

源地址:http://blog.163.com/bbluesnow126/blog/static/27784545201251051156817/ 链表相交问题 2012-06-10 17:15:37| 分类: 算法 | 标签:微软面试题 |字号 订阅 1、如何判断一个单链表有环 2、如何判断一个环的入口点在哪里 3、如何知…

VS 如何修改C++编译标准

第一步,打开项目资源管理器的属性页面 第二步,选择配置属性->C/C>语言->C语言标准 第三步,选择合适的标准,一般来说选最新即可

维吉尼亚密码和一次性密码本_密码学中的一次性密码

维吉尼亚密码和一次性密码本The One-time Pad cipher is almost similar to the Vernam cipher, as, like the vernam cipher, this cipher technique also encrypts the plain text by working on the binary level of the text. The only difference between the two is that…

十一、纺织面料下架功能的实现

一、数据库 数据库仍用yy_textile表,前几篇博文都叙述过这里就不再叙述 在fiber_yy数据库下创建yy_textile表 初始数据库信息 二、页面 admin_undercarriage 三、代码实现 admin_undercarriage using System; using System.IO; using System.Data; using S…

svg和canvas的应用场景分析【转载】

原文地址:http://blogs.msdn.com/b/weizhong/archive/2011/07/16/canvas-svg.aspx 思考什么时候使用Canvas 和SVG wzhong 15 Jul 2011 9:07 PM 0HTML5 Canvas 和 SVG 是 IE9 中引入的两项令人激动的图形功能。上周在拉斯维加斯举办的 MIX11 大会对这两个功能进行了介…

【C++grammar】文件系统以及path类使用

目录1.文件系统概述1、关于路径2、如何将某个路径下的所有文件递归地找出来?2.路径类及操作1、path类的成员函数2、path类的非成员函数示例1:展示C17中的path对象的用法示例2:展示Path类中用于分解路径成分的函数示例3:展示path相…

scala hashmap_如何在Scala中将Hashmap转换为Map?

scala hashmapLets first understand what are maps and hashmaps? 首先让我们了解什么是map和hashmap ? map in Scala is a collection that stores its elements as key-value pairs, like a dictionary. Scala中的map是一个集合,将其元素存储为键值…

十二、所有功能实现效果演示

一、系统项目架构 Ⅰ,fiber_yy数据库下有五张表 yy_admin:管理员登录账号和密码 yy_textile:纺织面料数据信息 yy_textile_record:用户购买纺织面料信息所存储的面料流水信息 yy_user:用户登录注册信息 yy_user_reco…

行业软件之PTV微观软件VISSIM4.3 5.0 5.1 5.2 5.3 5.4下载和相关资料

他是干什么的:http://baike.baidu.com/view/3656765.htm 中国代理销售的公司的网址:辟途威交通科技(上海)有限公司 官网:http://www.ptvchina.cn/ 看看视频中软件的运行效果:http://v.youku.com/v_show/id_XMzExMjg1MDEy.html 如何…

一、单个神经元网络构建

一、本人使用编译器为Jupyter Notebook,tensorflow版本为1.13.1 import tensorflow as tf print(tf.__version__) """ 1.13.1 """二、训练单个神经元网络 x为-1.0, 0.0, 1.0, 2.0, 3.0, 4.0 y为-3.0, -1.0, 1.0, 3.0, 5.0, 7.0 人用…

ruby 生成随机字符串_Ruby程序生成随机数

ruby 生成随机字符串产生随机数 (Generating random number) The task is to generate and print random number. 任务是生成并打印随机数。 Generating random numbers means that any number can be provided to you which is not dependent on any pre-specified condition…

leetcode 322. 零钱兑换 思考分析

目录1、题目2、思路分析3、参考链接1、题目 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 你可以认为每种硬币的数量是无限的。 提示: 1 …

linux上的英文字体monospace可以在windows用吗?

linux的字体都是开源的,应该可以官方下载本地下载转载于:https://www.cnblogs.com/52linux/archive/2012/03/14/2396103.html

Flash Builder 创建CSS

1.global 选择器将样式应用于所有控件 在 Flash Builder 中创建新MXML 文件并切换到设计模式 属性视图右侧的外观视图可更改外观 Flash Builder 自动创建CSS 文件 CSS 文件有2 个命名空间: s 指 Spark 组件 mx 指 MX 组件 1. Global 与Application 选择器 global …

ruby打印_Ruby程序打印数字的力量

ruby打印Ruby中数字的幂 (Power of a number in Ruby) The task to develop a program that prints power of a number in Ruby programming language. 开发可以用Ruby编程语言打印数字幂的程序的任务。 If we want to calculate the power of a number manually then we have…

二、训练fashion_mnist数据集

一、加载fashion_mnist数据集 fashion_mnist数据集中数据为28*28大小的10分类衣物数据集 其中训练集60000张,测试集10000张 from tensorflow import keras import tensorflow as tf import matplotlib.pyplot as plt import numpy as npfashion_mnist keras.data…

jquerymobile 切换页面时候闪烁问题

https://github.com/jquery/jquery-mobile/commit/acbec71e29b6acec6cd2087e84e8434fecc0053f 可以修改css好像是个bug -4,9 4,10 * Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.*/.spin {--webkit-animation-name: spin;--webkit-an…

二分法:两个有序数组长度为N,找到第N、N+1大的数

题目 两个有序数组长度为N,找到第N、N1大的数 思路1:双指针,O(N)复杂度 简述思路: 如果当前A指针指向的数组A的内容小于B指针指向的数组B的内容,那么A指针往右移动,然后nums(当前已经遍历过的数字个数)也…

Javascript -- In

http://www.caveofprogramming.com/articles/javascript-2/javascript-in-using-the-in-operator-to-iterate-through-arrays-and-objects/ http://msdn.microsoft.com/en-us/library/ie/9k25hbz2(vvs.94).aspx转载于:https://www.cnblogs.com/daishuguang/p/3392310.html

三、自动终止训练

有时候,当模型损失函数值预期的效果时,就可以结束训练了,一方面节约时间,另一方面防止过拟合 此时,设置损失函数值小于0.4,训练停止 from tensorflow import keras import tensorflow as tf import matplo…