设计模式六大原则
引言
本文主要讲解设计模式的设计原则,以及通过一些样例去详解(通篇还是偏稿子的形态,并没有好好整理,而是想到的就写下
六大原则
- 单一职责原则
- 开闭原则
- 里氏替换原则
- 依赖倒置原则
- 接口隔离原则
- 迪米特原则
单一职责
单一职责原则的定义(类、方法、接口)
首先先看概念
单一职责原则(Single Responsibility Principle,SRP)又称单一功能原则。这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)。
归纳总结,就是一个类不应该承担过多的职责,而是专注类自身的职责
优点
- 降低类的复杂度
- 提高类的可读性
- 提高系统的可维护性
- 降低变更的风险
样例
//user.js
class User {
...
login (username, password) {}
register (emal, username, password) {}
logError (msg)
sendEmail (email)
}
user类存在很大的问题,user既要去负责login行为,又要兼顾log日志和sendEmail,很明显后者的行为明显区别于前者。 因此需要去做拆分:
// user.js
class User {
...
login (username, password) {}
register (emal, username, password) {}
}
class LogSvc {
logError (msg)
}
class Email {
sendEmail (email)
}
直观的可以发现,颗粒度降低后,每个类都在做自己的特征职责。整体的代码逻辑就会非常清晰,并且修改email后,不需要QA再去回顾整个User类,而只需要去检查Email的功能即可
开闭原则
开闭原则(Open-Closed Principle, OCP)是指一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
强调的是用抽象构建框架,用实现扩展细节。
开闭原则,是面向对象设计中最基础的设计原则。它指导我们如何建立稳定灵活的系统。
示例
/**
* 顶层接口,定义了获取电脑信息的接口方法
*/
public interface Computer {
double getPrice();//价格
String getColor();//颜色
int getMemory();//内存
float getSize();//尺寸
}
然后定义两个实现类,华硕电脑与苹果Mac
/**
* 华硕
*/
public class AsusComputer implements Computer {
private double price;
private String color;
private int memory;
private float size;
public AsusComputer(double price, String color, int memory, float size) {
this.price = price;
this.color = color;
this.memory = memory;
this.size = size;
}
@Override
public double getPrice() { return this.price; }
@Override
public String getColor() { return this.color; }
@Override
public int getMemory() { return this.memory; }
@Override
public float getSize() { return this.size; }
}
/**
* Mac
*/
public class MacComputer implements Computer{
private double price;
private String color;
private int memory;
private float size;
public MacComputer(double price, String color, int memory, float size) {
this.price = price;
this.color = color;
this.memory = memory;
this.size = size;
}
@Override
public double getPrice() { return this.price; }
@Override
public String getColor() { return this.color; }
@Override
public int getMemory() { return this.memory; }
@Override
public float getSize() { return this.size; }
}
TC 如下
public class Test {
public static void main(String\[\] args) {
Computer computer = new AsusComputer(4888.88D,"深蓝",8,14.0F);
System.out.println(
"电脑:华硕\n" +
"售价:" + computer.getPrice() + "\n" +
"颜色:" + computer.getColor() + "\n" +
"内存:" + computer.getMemory() + "\n" +
"尺寸:" + computer.getSize()
);
}
}
电脑:华硕
售价:4888.88
颜色:深蓝
内存:8
尺寸:14.0
如果需要针对某个活动,对某个类型的电脑进行打折销售,需要对 getPrice 进行修改,可能会有如下改动:
@Override
public double getPrice() {
return this.price * 0.6;
}
这样不符合开闭原则,虽然看起来简单容易修改,但是不易拓展 正确的做法应该:
/**
* 华硕电脑打折
*/
public class AsusDiscountComputer extends AsusComputer {
private float discount;
public AsusDiscountComputer(double price, String color, int memory, float size, float discount) {
super(price, color, memory, size);
this.discount = discount;
}
public double getDiscountPrice(){
return getPrice() * this.discount;
}
}
应该考虑,该活动功能的拓展性,可以基于某一类电脑派生一个子类,实现对应改活动的折扣价格实现。一切要以可扩展出发
里氏替换原则
子类的继承实现,需要行为上保持和父类一致,如果行为不一致进行派生,将会造成程序上的错误
鸵鸟非鸟
根据生物学的定义鸵鸟实实在在是鸟类,有羽毛几乎覆盖全身的卵生脊椎动物,温血卵生,用肺呼吸,几乎全身有羽毛,后肢能行走,前肢变为翅,大多数能飞。定义一个鸟类,定义一个继承鸟类的鸵鸟类。
public class Bird
{
public virtual int FlySpeed{get;set;}
public void Fly() { }
}
public class Ostrich : Bird
{
public override int FlySpeed
{
get
{
return base.FlySpeed;
}
set
{
base.FlySpeed = 0;//不会飞 飞不起来
}
}
}
public class Bird
{
public virtual int FlySpeed{get;set;}
public void Fly() { }
}
public class Ostrich : Bird
{
public override int FlySpeed
{
get
{
return base.FlySpeed;
}
set
{
base.FlySpeed = 0;//不会飞 飞不起来
}
}
}
但是鸵鸟飞行速度为0,不能飞 所以程序必然会出错
总结
“LSP所表述的就是在同一个继承体系中的对象应该有共同的行为特征。我们在设计对象时是按照行为进行分类的,只有行为一致的对象才能抽象出一个类来。” 也就是说,行为不同的两个对象,不应该将某一个作为子类基于基类继承。鸵鸟非鸟,能飞是鸟的特性,但鸵鸟是不能飞的,我们强行将其归为鸟类,最终导致代码出错。
所有子类的行为功能必须和其父类持一致,如果子类达不到这一点,那么必然违反里氏替换原则。实际的开发过程中,不正确的派生关系会直接影响程序的运行。
里氏替换原则就是在设计时避免出现派生类与基类不一致的行为。
接口隔离原则
依赖倒置原则
https://www.cnblogs.com/TomXu/archive/2012/02/15/2330143.html
总结:高层模块的实现不应该依赖下层模块的实现,依赖倒置是让底层模块依赖高层模块
其他
多态
记录下多态,以一个例子说明
非多态例子
var makeSound = function(animal) {
if(animal instanceof Duck) {
console.log('嘎嘎嘎');
} else if (animal instanceof Chicken) {
console.log('咯咯咯');
}
}
var Duck = function(){}
var Chiken = function() {};
makeSound(new Chicken());
makeSound(new Duck());
多态例子
var makeSound = function(animal) {
animal.sound();
}
var Duck = function(){}
Duck.prototype.sound = function() {
console.log('嘎嘎嘎')
}
var Chiken = function() {};
Chiken.prototype.sound = function() {
console.log('咯咯咯')
}
makeSound(new Chicken());
makeSound(new Duck());
多态的理解:不同的对象做了相同的行为,但是可以得到不同的执行结果