博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
写给自己看的小设计4 - 对象设计通用原则之扩展原则
阅读量:4841 次
发布时间:2019-06-11

本文共 3768 字,大约阅读时间需要 12 分钟。

  除了前面学习的那些核心原则,还有一些衍生的原则,掌握它们,你将更好的面向对象。不妨称它们为"扩展原则"吧。
迪米特法则:尽量不与无关的类发生关系。
  迪米特法则全称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关系,也就是说如果某个类只是作为另一个类的从属关系存在的时候,就可以使用组合了。
  注意这句话中的"宽松",组合使用起来就是可以这么"任性"。
  对于很多的功能,其实纯用继承也是可以实现的,但是总是不完美,要么有冗余成员,要么复用程度不够,这个时候基本就说明单纯的继承是不够的,可以尝试使用"组合+继承"的方式。
 
  好了,大原则中能上台面的也就是这么多了。

转载于:https://www.cnblogs.com/dxy1982/p/4293978.html

你可能感兴趣的文章
46.纯css实现瀑布流(flex)
查看>>
Jmeter学习过程中遇到的那些坑
查看>>
PHP程序员的技术成长之路规划
查看>>
poj 2887 Big String(块状链表)
查看>>
BZOJ 2002: [Hnoi2010]Bounce 弹飞绵羊(LCT裸题)
查看>>
lintcode-101-删除排序数组中的重复数字 II
查看>>
【BZOJ1925】 [SDOI2010] 地精部落(带有一堆性质的动态规划)
查看>>
python中urllib的urlencode与urldecode
查看>>
JS-基础-01.变量、基本数据类型
查看>>
iOS ---------NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
查看>>
命名空间“System.Web”中不存在类型或命名空间名称“Optimization”(是否缺少程序集引用?)...
查看>>
Python异常 --Python
查看>>
Struts数据验证
查看>>
第十二章 动态内存
查看>>
句柄1
查看>>
vb.net入门+vs2010(20180208)
查看>>
一般jsp 翻页 选择 保留 代码
查看>>
操作系统环境变量LANG和NLS_LANG的关系
查看>>
存储过程简单Demo
查看>>
查看oracle实例名
查看>>