Change Reference to Value (将引用对象改为值对象)

Summary:

你有一个引用对象,很小且不可变,而且不易管理。将它变成一个值对象。

Motivation:

如果引用对象开始变得难以使用,也许就应该将它改为值对象。引用对象必须被某种方式控制,你总是必须向其控制者请求适当的引用对象。它们可能造成内存区域之间错综复杂的关联。在分布系统和并发系统中,不可变的值对象特别有用,因为你无需考虑它们的同步问题。

值对象有一个非常重要的特性:它们应该是不可变的。无论何时,只要你调用同一个对象的同一个查询函数,都应该得到同样的结果。如果保证了这一点,就可以放心地以多个对象表示同一个事物。如果值对象是可变的,就必须确保对某一对象的修改会自动更新其他“代表相同事物”的对象。这台痛苦了,与其如此还不如把它变成引用对象。

这里有必要澄清一下“不可变”的意思。如果以Money类表示“钱”的概念,其中有“币种”和“金额”两条信息,那么Money对象通常是一个不可变对象。这并非意味着薪资不能变,而是意味着:如果要改变薪资,就需要使用另一个Money对象来取代现有的Money对象,而不是在现有的Money对象上修改。你和Money对象之间的关系可以改变,但是Money对象自身不能改变。

Mechanics:

1.检查重构目标是否为不可变对象,或是否可修改为不可变对象。

如果该对象目前还不是不可变的,就使用Remove Setting Method,直到它成为不可变为止。

如果无法将该对象修改为不可变的,就放弃使用本项重构。

2.建立equals() 和hashCode().

3.编译,测试。

4.考虑是否可以删除工厂函数,并将构造函数声明为public。

范例

        我们从一个表示“货币种类”的Currency类开始:

public class Currency
{
    private String code;

    public String getCode()
    {
        return code;
    }

    private Currency( String code )
    {
        this.code = code;
    }
}

这个类所做的就是保存并返回一个货币种类代码。它是一个引用对象,所以如果要得到它的实例,必须这么做:

Currency usd = Currency.get("USD");
Currency类维护一个包含所有Currency实例的链表。我们不能直接使用构造函数创建实例,因为Currency构造函数是private的。

要把一个引用对象变成值对象,关键动作是:检查它是否可变。如果不是,就不能使用本项重构,因为可变的值对象会造成烦人的别名问题。

在这里,Currency对象是不可变的,所以下一步就是为它定义equals();

public boolean equals( Object arg )
 {
     if( !( arg instanceof Currency ) )
     {
          return false;
     }
     Currency other = ( Currency ) arg;
     return ( code.equals( other.getCode() ) );
}

定义了equals(),就必须同时定义hashcode()。实现hashcode() 有个简单办法:读取equals()使用的所有字段的hash码,然后对它们进行按位异或(^)操作。本例中,这很容易实现,因为equals()只是用了一个字段:

public int hashCode()
{
    return code.hashCode();
}
完成这两个函数后,我们可以编译并测试。这两个函数的修改必须同时进行,否则依赖hash的任何集合对象(例如Hashtable、HashSet和HashMap)都可能产生意外行为。

现在,我们可以创建任意多个Currency对象,还可以把构造函数声明为public,直接以构造函数获取Currency实例,从而去掉Currency类中的工厂函数和控制实例创建行为。


转载于:https://my.oschina.net/u/134516/blog/131506

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Chapter 1:Refactoring,a First Example 重构,第一个例子   The Starting Point 起点   The First Step in Refactoring 重构第一步   Decomposing and Redistributing the Statement Method 分解并重组slalemenl方法   Replacing the Conditional Logic on Price Code with Polymorphism 用多态代替价格条件逻辑代码   Final Thoughts 结语  Chapter 2:Principles in Refactoring 重构原则   Defining Refactoring 何谓重构   Why Should You Refactor? 为何重构   When Should You Refactor? 何时重构   What Do I Tell My Manager? 怎样说服经理   Problems with Refactoring 重构的问题   Refactoring and Design 重构与设计   Refactoring and Performance 重构与性能   Where Did Refactoring Come From? 重构的起源  Chapter 3:Bad Smells in Code(by Kent Beck and Martin Fowler) 代码坏昧   Duplicated Code 重复代码   Long Method 过长方法   Large Class 过长类   Long Parameter List 过长参数列表   Divergent Change 发散式变化   Shotgun Surgery 霰弹式修改   Feature Envy 特性依恋   Data Clumps 数据泥团   Primitive Obsession 基本类型偏执   Switch Statements switch语句   Parallel Inheritance Hierarchies 平行继承体系   Lazy Class 冗余类   Speculative Generality 理论上的一般性   Temporary Field 临时字段   Message Chains 消息链   Middle Man 中间人   Inappropriate Intimacy 过度亲密   Alternative Classes with Different Interfaces 接口不同的等效类  Incomplete Library Class 不完整的库类   Data Class 数据类   Refused Bequest 拒绝继承   Comments 注释过多  Chapter 4:Building Tests 构建测试   The Value of Self-testing Code 自测试代码的重要性   The JUnit Testing Framework Junit测试框架   Adding More Tests 添加更多测试  Chapter 5:Toward a Catalog of Refactorings 重构目录   Format of the Refactorings 重构描述的格式   Finding References 寻找引用   How Mature Are These Refactorings? 这些重构的成熟度如何 Chapter 6:Composing Methods 组合方法   Extract Method 提取方法   Inline Method 内联方法   Inline Temp 内联临时变量   *Replace Temp with Query 用查询方法代替临时变量   Introduce Explaining Variable 引入解释性变量   Split Temporary Variable 分离临时变量   *Remove Assignments to Parameters 去除参数赋   Replace Method with Method Object 用方法对象代替方法   Substitute Algorithm 替换算法  Chapter 7:Moving Features Between Objects 在对象之间移动特性  *Move Method 移动方法   Move Field 移动字段   Extract Class 提取类   Inline Class 内联类   Hide Delegate 隐藏委托类   Remove Middle Man 去除中间人   Introduce Foreign Method 引入外加方法   *Introduce Local Extension 引入本地扩展类  Chapter 8:Organizing Data 组织数据   Self Encapsulate Field 自封装字段   Replace Data Value with Object 用对象代替数据   Change Value to Reference对象改为引用对象   Change Reference to Value引用对象改为对象   Replace Array with Object 用对象代替数组   Duplicate Observed Data 重复被观察数据   *Change Unidirectional Associationto Bidirectional 将单向关联改为双向   Change Bidirectional Association to Unidirectional 将双向关联改为单向   *Replace Magic Number with Symbolic Constant 用字面常量代替魔数   Encapsulate Field 封装字段   Encapsulate Collection 封装集合   Replace Record with Data Class 用数据类代替记录   *ReplaceType Code with Class 用类代替类型码   Replace Type Code with Subclasses 用子类代替类型码   Replace Type Code with State/Strategy用State/Strategy 代替类型码   Replace Subclass with Fields 用字段代替子类  Chapter 9:Simplifying Conditional Expressions 简化条件语句   Decompose Conditional 分解条件语句   Consolidate Conditional Expression 合并条件语句   Consolidate Duplicate Conditional Fragments 合并重复的条件片段   Remove Control Flag 去除控制标志   Replace Nested Conditional with Guard Clauses 用守卫语句代替嵌套条件语句   Replace Conditional with Polymorphism 用多态代替条件语句   Introduce Null Object 引入Null对象   Introduce Assertion 引入断言  Chapter 10:Making Method Calls Simpler 简化方法调用   Rename Method 重命名方法   Add Parameter 添加参数   Remove Parameter 去除参数   Separate query from Modifier 将查询方法与修改方法分离   Parameterize Method 参数化方法   Replace Parameter with Explicit Methods 用显式方法代替参数  Preserve Whole Object 保持对象完整   Replace Parameter with Method 用方法代替参数   Introduce Parameter Object 引入参数对象   Remove Setting Method 去除设置方法   Hide Method 隐藏方法   Replace Constructor with Factory Method 用工厂方法代替构造器   Encapsulate Downcast 封装向下转型   Replace Error Code with Exception 用异常代替错误码   Replace Exception with Test 用测试代替异常  Chapter 11:Dealing with Generalization 处理泛化关系   Pull Up Field 上移字段   Pull UP Method 上移方法   Pull Up Constructor Body 上移构造器主体   Push Down Method 下移方法   Push Down Field 下移字段   Extract Subclass 提取子类   Extract Superclass 提取超类   Extract Interface 提取接口   Collapse Hierarchy 合并继承层次   Form Template Method 形成Template Method   Replace Inheritance with Delegation 用委托代替继承   Replace Delegation with Inheritance 用继承代替委托  Chapter 12:Big Refactorings(by Kent Beck and Martin Fowler) 大型重构   Tease Apart Inheritance 分解继承层次   Convert Procedural Design to Objects 将过程式设计转换为面向对象   Separate Domain from Presentation 将领域逻辑与表现分离   Extract Hierarchy 提取继承层次  Chapter 13:Refactoring,Reuse,and Reality(by William Opdyke) 重构,复用与现实   A Reality Check 现实的检验   Whv Are Developers Reluctant to Refactor Their Programs? 开发人员为何不愿重构程序   A Reality Check(Revisited) 再谈现实的检验   Resources and References for Refactoring 重构的资源和参考文献   Implications Regarding Software Reuse and Technology Transfer 对软件复用与技术传播的意义   A Final Note 结语   References 参考文献  Chapter 14:Refactoring Tools(by Don Roberts and John Brant) 重构工具   Refactoring with a Tool 使用工具重构   Technical Criteria for a Refactoring Tool 重构工具的技术标准   Practical Criteria for a Refactoring Tool 重构工具的实用标准   Wrap Up 结语  Chapter 15:Putting It All Together(by Kent Beck) 集大成  References参考文献  List of Soundbites 要点列表  Updates 更新内容  Index 索引
要将轨迹变为椭圆,你需要修改预定轨迹的生成方式。假设你想要生成一个长轴为 a,短轴为 b 的椭圆,可以使用如下代码: ``` a = 1; % 长轴 b = 0.5; % 短轴 t = linspace(0, 2*pi, 2001); % 生成一个周期为 2*pi 的时间序列 xd = a*cos(t); % x 轴坐标 yd = b*sin(t); % y 轴坐标 ``` 要使控制器稳定跟踪椭圆轨迹,你需要使用一个更加复杂的控制算法,比如模型参考自适应控制(Model-Reference Adaptive Control,MRAC)或自适应控制(Adaptive Control,AC)。这里给出一个简单的 MRAC 控制器的示例: ``` clear all; close all; ts = 0.001; a = 1; % 椭圆长轴 b = 0.5; % 椭圆短轴 t = linspace(0, 2*pi, 2001); % 生成一个周期为 2*pi 的时间序列 xd = a*cos(t); % x 轴坐标 yd = b*sin(t); % y 轴坐标 thd = atan2(-b*sin(t), a*cos(t)); % 计算椭圆轨迹上点的角度 for k=1:1:2001 u1(k) = 0; u2(k) = 0; e1(k) = 0; e2(k) = 0; e3(k) = 0; end y0 = [1;0;pi/2]; M = 20; theta = [0;0;0;0;0;0]; % MRAC 参数 for i=0:1:M pause(0.001); for k=1:1:2001 if k==1 q=y0; end xp(k) = q(1); yp(k) = q(2); th(k) = q(3); qd = [xd(k);yd(k);thd(k)]; ce1(k) = qd(1)-q(1); ce2(k) = qd(2)-q(2); ce3(k) = qd(3)-q(3); u = [u1(k);u2(k)]; B = ts*[cos(q(3)) 0 sin(q(3)) 0 0 1; 0 cos(q(3)) 0 sin(q(3)) -1 0]; L1 = [theta(1) theta(2) 0; 0 0 theta(3)]; L2 = [theta(4) theta(5) 0; 0 0 theta(6)]; cond = norm(eye(2)-L1*B); U = u+L1*[e1(k);e2(k);e3(k)]+L2*[ce1(k);ce2(k);ce3(k)]; u1(k) = U(1); u2(k) = U(2); u = [u1(k);u2(k)]; q = q+B*u; e1(k) = cos(k*ts*pi)-q(1); e2(k) = sin(k*ts*pi)-q(2); e3(k) = ts*k*pi+pi/2-q(3); if i > 0 x = [e1(k); e2(k); e3(k); xd(k); yd(k); thd(k)]; dx = [B*u; 0; 0]; dtheta = -0.5*sign(cond)*L1*(dx-x'*B')*x; theta = theta + dtheta*ts; end end figure(1); hold on; plot(xd, yd, 'r', xp, yp, 'b'); xlabel('xd xp');ylabel('yd,yp'); j = i+1; times(j) = j-1; e1i(j) = max(abs(e1)); e2i(j) = max(abs(e2)); e3i(j) = max(abs(e3)); end figure(2); plot(xd, yd, 'r', xp, yp, 'b'); xlabel('xd xp');ylabel('yd,yp'); figure(3); plot(times, e1i, '*-r', times, e2i, 'o-b', times, e3i, 'o-k'); title('Change of maximum absolute value of e1,e2 and angle with times i'); xlabel('times');ylabel('e1,e2 and angle'); ``` 这个示例中,我们计算出了椭圆轨迹上点的角度 thd,并在 MRAC 控制器中增加了 MRAC 参数 theta 的计算和更新。MRAC 控制器通过在线学习和调整参数 theta 来逐步优化控制效果,实现稳定跟踪椭圆轨迹的目标。需要注意的是,MRAC 控制器需要精细调整参数和控制算法,才能保证控制效果的稳定性和收敛性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值