设计模式之六大设计原则学习笔记

六大设计原则学习笔记

单一职责原则

应该有且仅有一个原因引起累的变更。
tips:当有一个类有多个职责可以实现多个接口。

里氏替换原则

继承的特点:

  • 代码共享,提高代码的重用性,
  • 提高代码的可扩展性
  • 侵入性(子类必须有父类所以的属性和方法),降低了灵活性
  • 增强了耦合性

里氏替换原则的定义:

所有引用基类的地方必须能透明地使用其子类的对象 
  • 子类必须完全实现父类的方法
  • 子类可以有自己的个性
  • 覆盖或实现父类的方法时输入参数可以被放大
  • 覆写或实现弗雷德方法时输出结果可以被缩小

依赖倒置原则

依赖倒置原则的含义:

  • 高层模块不应该依赖底层模块,二者都应该依赖其抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象

在java中的表现:

  • 模块间的依赖通过抽象发生,实现类之间发生直接的依赖关系,,其依赖关系通过接口或抽象类产生
  • 接口或抽象类不依赖于实现类
  • 实现类依赖接口或抽象类

例:以人或者司机开汽车为例。

//司机接口
public interface IDriver {
	public void driver(ICar car);
}
//司机实现
public class Driver implements IDriver{
	@Override
	public void drive(ICar car) {
		car.run();
	}
}
//汽车接口
public interface ICar {
	public void run();
}
//奔驰车实现
public class Benz implements ICar{
	@Override
	public void run() {
		System.out.println("Benz车正在运行...");
	}
}
//宝马车实现
public class Bmw implements ICar{
	@Override
	public void run() {
		System.out.println("Bmw车正在运行...");
	}
}
/**
 * 张三开奔驰车
 */
public class Client1 {
	public static void main(String[] args) {
		IDriver ZhangSan = new Driver();
		ICar benz = new Benz();
		ZhangSan.drive(benz);
	}
}
/**
 * 张三开宝马车
 */
public class Client2 {
	public static void main(String[] args) {
		IDriver ZhangSan = new Driver();
		ICar bmw = new Bmw();
		ZhangSan.drive(bmw);
	}
}

从上面的列子我们可以看出,高层模块,如client和driver,不依赖底层模块的,只是依赖接口,如,driver中的car对象只是以ICar声明,通过接口产生;client中也是通过IDriver和ICar来声明对象。

依赖的三种写法:

  • 构造函数传递依赖对象

      public interface IDriver {
      	public void driver();
      }
      
      public class Driver implements IDriver{
      	private ICar car;
      	//构造函数注入
      	public Driver(ICar _car){
      		this.car = _car
      	}
      	@Override
      	public void driver() {
      		this.car.run();
      	}
      }
    
  • Setter方法传递依赖对象

      public interface IDriver {
      	public void setCar(ICar car);
      	public void driver();
      }
      
      public class Driver implements IDriver{
      	private ICar car;
      	//Setter方法
      	public setCar(ICar _car){
      		this.car = _car
      	}
      	@Override
      	public void driver() {
      		this.car.run();
      	}
      }
    
  • 接口声明依赖对象

      //这段的第一段很长的程序,就是这种情况
      public interface IDriver {
      	public void driver(ICar car);
      }
      public class Driver implements IDriver{
      	@Override
      	public void drive(ICar car) {
      		car.run();
      	}
      }
    

接口隔离原则

定义:

  • 客户端不应该依赖它不需要的接口
  • 类间的依赖关系应该建立在最小的接口上

原则:

  • 接口要尽量小

    这是接口隔离原则的核心定义,不出现臃肿的接口(Fat Interface),但是“小”是有限度的,首先就是不能违反单一职责原则。根据接口隔离原则拆分接口时,首先必须满足单一职责原则。

  • 接口要高内聚

    高内聚就是要提高接口、类、模块的处理能力,减少对外的交互。具体到接口隔离原则就是,要求在接口中尽量少公布public方法,接口是对外的承诺,承诺地越少对系统开发越有利,变更的风险也就越少,同时也有利于降低成本。

  • 定制服务

    定制服务就是单独为一个个体提供优良的服务。

  • 接口设计是有限度的

    接口的设计粒度越小,系统越灵活,这是不争的事实。但是,灵活的同时也带来了结构的复杂化,开发难度增加,可维护性降低,这不是一个项目或产品所期望看到的,所以接口设计一定要注意适度,这个度只能根据经验和常识判断,没有一个固化或可测量的标准。

迪米特法则LOD(最少知识原则LKP)

一个对象应该对其他对象有最少的了解
一个类应该对自己需求耦合或调用的类知道的最少,你(被耦合或调用的类)的内部是多么的复杂都和我没有关系
低耦合

开闭原则

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

在设计一个模块时,应当使这个模块可以在不被修改的前提下被扩展,换言之,应当可以在不必修改源代码的情况下改变这个模块的行为;因为所有软件系统中 有一个共同的特性,即它们的需求都会随时间的推移而发生变化,在软件系统面临新的需 求时,满足开-闭原则的软件中,系统已有模块(特别是最重要的抽象层)不能再修改,而 通过扩展已有的模块(特别是最重要的抽象层),可以提供新的行为,以满足需求。

开-闭原则如果从另一个角度讲述,就是所谓可变性封装原则(Principle of Encapsulation of Variation,略写作EVP),找到系统的可变因素,将之封装起来。 也就是:考虑你的设计中有什么可能发生变化,允许这些变化而不让这些变化 导致重新设计。可变性封装原则意味着:

一种可变性不应当散落在代码的很多角落,而应当被封装到一个对象中,同一种可变性 的不同表现可以体现在子类中,继承应当被看做是封装变化的方法,而不仅仅看做是从父类派生子类
一种可变性不应当与另一种可变性混合在一起,所以一个设计模中,类图的继承层次 不会超过两层,不然就意味着将两种可变性混在一起

做到开闭原则不是件容易的事,但也很多规律可循,这些规律也同样以设计原则的身份 出现,它们都是开-闭原则的手段和工具,是附属于开-闭原则的。
例:模拟书店销售书籍为例

public interface IBook {
	public String getName();
	public int getPrice();
	public String getAuthor();
}

public class NovelBook implements IBook {
	private String name;
	private int price;
	private String author;
	@Override
	public String getName() {
		return this.name;
	}
	@Override
	public int getPrice() {
		return this.price;
	}
	@Override
	public String getAuthor() {
		return this.author;
	}
	public NovelBook (String _name, int _price, String _author){
		this.name = _name;
		this.price = _price;
		this.author = _author;
	}
}

public class BookStore {
	private final static ArrayList<IBook> booklist = new ArrayList<IBook>();
	static{
		booklist.add(new NovelBook("天龙八部",32,"金庸"));
		booklist.add(new NovelBook("巴黎圣母院",40,"雨果"));
		booklist.add(new NovelBook("悲惨世界",43,"雨果"));
	}
	public static void main(String[] args) {
		System.out.println("------ 模拟书店卖出去的书记录如下 ------");
		for (IBook book:booklist) {
			System.out.print("--- 卖出书:"+book.getName());
			System.out.print("---价格:"+book.getPrice());
			System.out.println("---作者:"+book.getAuthor());
		}
	}
}

然而,现在由于需要而需将书打折出售,原价大于40元的9折,原价小于40的八折。

有三种方法解决这类问题

  • 修改接口
  • 修改实现类(有时候不适用)
  • 通过扩展实现变化

对于上面的例子,解决办法如下:

  • 对于第一种,可以修改iBook接口,添加getOffPrice()方法
  • 对于第二种,可以修改NovelBook中的getPrice方法
  • 对于第三种,,添加一个OffNovelBook实现iBook接口

posted on 2014-12-09 18:38  凡高是我吗  阅读(388)  评论(0编辑  收藏  举报

导航