写给自己看的小设计4 - 对象设计通用原则之扩展原则

  除了前面学习的那些核心原则,还有一些衍生的原则,掌握它们,你将更好的面向对象。不妨称它们为"扩展原则"吧。

迪米特法则:尽量不与无关的类发生关系。

  迪米特法则全称Law of Demeter,简称LoD,也称为最少知识原则(Least Knowledge Principle,LKP)。这个原则没什么固定的定义,大体上有这么几种说法:

1. 只与你的朋友说话
2. 不和陌生人说话
3. 对象应该只与必须交互的对象通信

  通俗地讲,一个类应该对自己需要调用的类知道得最少,你调用的类的内部是如何实现的,都和我没关系,那是你的事情,我就知道你提供的接口方法,我就调用这么多,其他的一概不关心。

  也可以说,不要让类也染上人们之间的那种神秘的暧昧关系。对象之间联系越是简单,则越是容易管理。

  具体的从技术上来说,就是要求对象只与下列必须交互的各类对象通信:

(1) 当前对象本身(this);
(2) 以参数形式传入到当前对象方法中的对象;
(3) 当前对象的成员对象;
(4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是可以交互的;
(5) 当前对象所创建的对象。

  此外,还需要适当设置对象的访问权限。

  正面教材太多了,就看一些反面教材吧:

// 方法链式调用,此种方式在Web页面开发中倒是常用,但是静态语言中似乎不推荐
public void Do()
{
  m_accessor.GetUser().Rename();
}
// 无谓的公开方法
class UI
{
 public void Do()
 {
  WorkHelper();
 }

 public void WorkHelper() { }
}

好莱坞法则:不要调用我,让我调用你。

  在前面我们分析对象之间交互的时候,直接调用指的就是直接调用对象的方法,间接调用中很重要的一种就是回调,特别是在异步编程中,好莱坞法则从某种程度上来说,就是等同于在合适的时候,多使用回调函数。

  下面是C#版本的事件实现:

public class Program
{
 static void Main(string[] args)
 {
  User user = new User();
  View ui = new View(user);
  user.Name = "Hello";
 }
}

delegate void OnNameChange(string name);
class View
{
 public View(User user)
 {
  user.onNameChanged += user_onNameChanged;
 }

 void user_onNameChanged(string name)
 {
  Console.WriteLine(name);
 }
}

class User
{
 private string m_name;
 public string Name
 {
  get { return m_name; }
  set
  {
   m_name = value;
   onNameChanged(m_name);
  }
 }
 public event OnNameChange onNameChanged;
}

  这也是简单的MVC模式中的MV之间的交互方式,View作为事件的接收者,只需要提供好回调函数,当Model部分发生变化时,View自动接收到变化去更新UI(此处只是打印了出来)。

  如果这里不使用事件(观察者模式)来实现,那么Model必然要保存View的引用(实际上内部当然还是保存了相关引用的,但是观察者合理采用各种抽象手段安排好了引用管理,比如这个例子中delegate的使用,作为C#中相当弱的耦合关系,它远比直接使用继承,实现接口的耦合性要弱的多),当Model的数据发生变化时,直接调用View的相关方法去更新UI,这种强烈的互相依赖关系对程序来说并不是什么好的做法。而且一旦多个未确定的类似于View的角色对Model的改变感兴趣的时候,直接应用常常难于处理。

  电影中常说,单线联系最安全,如此是也。

优先使用组合原则:多使用组合,少使用继承

  复用的手段除了继承这种强约束手段,组合这种弱耦合的关系更加灵活。

  看一个小例子:

class User
{
 public virtual void PrintType() { }
}
class Admin : User
{
 public override void PrintType() { Console.WriteLine("Employer"); }
}
class Programmer : User
{
 public override void PrintType() { Console.WriteLine("Employer"); }
}
class Manager : User
{
 public override void PrintType() { Console.WriteLine("Employer"); }
}
class Contractor : User
{
 public override void PrintType() { Console.WriteLine("Temp"); }
}

  公司的系统中除了Contractor外几乎全是正式员工,打印类型的时候只需要打印Employer即可,而只有Contractor需要打印Temp。

  针对这个功能,如果我们设计一个类层次像上面这样,工作是完全正常的。而且当有新的正式员工类型的话,也只需要复制一遍Admin的PrintType方法即可,这里没有违背任何的我们前面介绍的基本或者核心原则。但是我们还是发现了不爽的地方,那就是打印正式员工的代码复制的到处都是,咋办?还是老套路,抽象加封装,再传进来

public class Program
{
 static void Main(string[] args)
 {
  User admin = new Admin(new EmployerPrintor());
  admin.PrintType();
  User contractor = new Contractor(new TempPrintor());
  contractor.PrintType();
 }
}

class User
{
 Printor m_printor;
 public User(Printor printor)
 {
  m_printor = printor;
 }

 public virtual void PrintType() { m_printor.PrintType(); }
}
class Admin : User
{
 public Admin(Printor printor)
  :base(printor)
 {

 }
}
class Contractor : User
{
 public Contractor(Printor printor)
  : base(printor)
 {

 }
}

class Printor
{
 public virtual void PrintType() { }
}
class EmployerPrintor : Printor
{
 public override void PrintType() { Console.WriteLine("Employer"); }
}
class TempPrintor : Printor
{
 public override void PrintType() { Console.WriteLine("Temp"); }
}

  对于这个原则,其实我自己宁愿描述为:合理使用继承与组合。作为复用和描述对象关系的两种最基本的手段,我想说的是适合继承的使用场景时候还是得用继承,适合使用组合的时候就使用组合。

  个人认为:

  继承的使用场景:满足严格的IS-A关系,也就是说当基类是真正的作为子类的强约束存在时,也即子类完全复用基类的所有信息的时候,继承是必须的。

  注意这句话中的"严格"和"强约束",继承作为一种最为沉重的复用关系,使用继承时要多加考虑,因为现代语言大多数都是单继承(只能继承一个类)、多实现(可以实现多个接口)的使用方式,一旦从类的继承关系被使用了以后,扩展性其实是被限制在了基类的范围内了。但是一旦确定需要它,就放下顾虑,直接使用。其实在前面的所有例子中,我们几乎每个例子中都离不开继承。

  组合的使用场景:满足宽松的HAS-A关系,也就是说如果某个类只是作为另一个类的从属关系存在的时候,就可以使用组合了。

  注意这句话中的"宽松",组合使用起来就是可以这么"任性"。

  对于很多的功能,其实纯用继承也是可以实现的,但是总是不完美,要么有冗余成员,要么复用程度不够,这个时候基本就说明单纯的继承是不够的,可以尝试使用"组合+继承"的方式。

  好了,大原则中能上台面的也就是这么多了。

时间: 2024-10-29 19:08:54

写给自己看的小设计4 - 对象设计通用原则之扩展原则的相关文章

写给自己看的小设计5 - 对象设计过程之设计目标

浏览完代码设计中对象设计的核心准则和扩展原则后,最后我们再回头看看另外一个原则不像原则,规则不像规则,模式不像模式,实践不像实践的四不像原则 - GRASP原则. 说到对象设计原则,就不能不提GRASP原则,但是从我来看,这个原则完全不是原则,而是设计过程. GRASP,全称为General Responsibility Assignment Software Pattern,即通用职责分配软件模式,它由<UML和模式应用>(Applying UML and Patterns)一书作者Crai

写给自己看的小设计3 - 对象设计通用原则之核心原则

由于对象设计的核心是类,所以下面的原则也都基本都是讨论类的设计问题,其它类型的元素都比较简单,基本上也符合大多数这里列出的原则. 前面我们分析完了对象设计的基本原则,这里我将重新温习一下对象设计的核心原则 - SOLID原则.几乎所有的设计模式都可以看到这些原则的影子. 单一职责原则(SRP):做一个专一的人 单一职责原则的全称是Single Responsibility Principle,简称是SRP.SRP原则的定义很简单: 即不能存在多于一个导致类变更的原因.简单的说就是一个类只负责一项

写给自己看的小设计7 - 对象设计过程之对象交互

对象创建完了以后,就是互相协作完成系统的功能.对象的协作方式通常有如下方式: 直接引用,互通有无 这种方式最为自然,最为直接,最为简单,也是通常情况下的首选.不管是传参数,还是直接创建后直接使用对象的方法,都是属于这种情况: public class ComponentB { public void Run(ComponentA componentA) { componentA.Say(); } } 依靠中介通信 当对象之间的交互复杂起来以后,直接的通信可能耦合度就太高了,这个时候要靠辅助对象来

写给自己看的小设计6 - 对象设计过程之对象创建

对象创建是面向对象程度的最常见活动之一.对象的创建通常有两种方式:直接创建,或者是间接创建. 直接创建对象 直接创建意味着由使用对象的元素直接创建对象,然后使用对象.这种方式最常用,也是对象之间建立耦合的最常见方式,也是如非必要,优先考虑的对象创建方式.例如下面的C#代码: public class ComponentA { // 类的成员直接new出来 ComponentB m_componentB = new ComponentB(); public ComponentA() { } pub

读书笔记:《写给大家看的设计书》

读书笔记:<写给大家看的设计书> <写给大家看的设计书>这本书本来是买给孩子看的,孩子对板报.杂志.名片等设计很感 兴趣,想看点基础的设计类的书籍,就给她找了一本.书到手后,我随手翻了翻发现对于我制作PPT还是很有帮助的,对于非专业设计人员来说,掌握4条设计原 则确实可以让设计感觉到非常专业,这几条原则应用于网站的设计也是同样有效. 全书三大部分,共14章,第一部分(第1-8章)最有用,讲述四大设计原则,第二部分(第9-11章)讲字体设计,第三部分有点像附录. 第一章 约书亚树 有

写给大家看的设计书(第3版)PDF下载高清完整扫描原版

这本书出自一位世界级设计师之手.复杂的设计原理在书中凝炼为亲密性.对齐.重复和对比4 个基本原则.作者以其简洁明快的风格,将优秀设计所必须遵循的这4 个基本原则及其背后的原理通俗易懂地展现在读者面前.本书包含大量的示例,让你了解怎样才能按照自己的方式设计出美观且内容丰富的产品. 此书适用于各行各业需要从事设计工作的读者,也适用于有经验的设计人员.需要学习的朋友可以通过网盘下载pdf版 http://putpan.com/fs/8y1i5bce5n5s1h8u0/ 作者简介 Robin Willi

写给大家看的设计书——读后笔记

<写给大家看的设计书>介绍了设计的四个基本原则:亲密性.对齐.重复.对比.作为一个软件"设计师",我也来聊聊读过这本书之后,我对这四个原则的一点理解. 亲密性 亲密性原则是指:内涵相关联的内容,在结构.关系上也应保持关联.        以软件设计的角度来说,一项业务所包含的功能.一个功能所包含的代码,应该在结构.关系上保持关联.例如把这些代码放到同一个包下.用同一套规则来命名.这样,当我们需要查阅.修改这个功能,需要处理哪些代码就"一望而知"了.   

读书笔记:《写给大家看的面向对象设计》,《程序员的职业素养》,《设计模式其实很简单》

按照上次的计划 看了三本书,笔记现在才贴出来. <写给大家看的面向对象设计>: 使用接口开发的作用 规范函数命名,特别在项目人数比较多,在设计时,定了接口命名与参数. 可以把前台与后台的脱离.定义接口后,实现接口并返回模拟的数据,例如DataTable等,前台不需等后台就可以做UI与交互,改善UI与需要的数据,发现问题并不断完善接口.后台按照需求把数据库设计好了(如果是领域驱动开发,是Model创建),按照这个接口来开发功能,完成之后前台切换过来即可.前后台是并行开发. 便于单元测试的编写,其

写给大家看的编程规范

(本文参加 2014 CSDN博文大赛,谢谢.) [文章摘要] "没有规矩,不成方圆",在实际的软件开发项目中,做任何事情都不是随心所欲的,我们编写代码需要遵守项目组约定的编程规范.很遗憾,在学校的计算机课程中,重在教导学生实现一定的程序功能,对程序的编写规范很少提及,这也就导致了从学校毕业踏上工作岗位之后一段艰辛的学习过程. 本文根据自身的软件开发实践,对实际的软件开发项目中编写C语言和SQL语言程序时所需遵守的规范进行了详细的介绍,旨在让广大即将从事软件开发工作的程序员们懂得编程规