设计模式原则就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式依据什么设计出来)。设计模式常用的七大原则有:
1.单一职责原则
2.开放封闭原则
3.里氏代换原则
4.依赖倒转原则
5.合成/聚合复用原则
6.接口隔离原则
7.迪米特法则
一.单一职责原则
- 就一个类而言,应该仅有一个引起它变化的原因(职责)
- 单一职责原则对类来说,即一个类应该只负责一项职责。
- 如类A负责两个不同职责:职责1,职责2,当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A 的粒度分解为A1,A2
- 主要根据不同的角度划分职责
实例:交通工具类
方案一:
run方法中,违反了单一职责原则,修改vehicle类
class vehicle
{
public void run(String vehicle)
{
System.out.println(vehicle+"在公路上跑");
}
}
public class singleresponsibility {
public static void main(String[] args) {
// TODO Auto-generated method stub
vehicle s=new vehicle();
s.run("汽车");
s.run("火车");
s.run("飞机");
}
}
修改分析:
1.这种修改方法没有对原来的类做大的修改,只是增加方法;
2.增加的部分不影响原有部分,降低了变更引起的风险;
3.这里在类这个级别上没有遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责。
class vehicle
{
public void run(String vehicle)
{
System.out.println(vehicle+"在公路上跑");
}
public void fly(String vehicle)
{
System.out.println(vehicle+"在天上飞");
}
public void swim(String vehicle)
{
System.out.println(vehicle+"在水里游");
}
}
public class singleresponsibility {
public static void main(String[] args) {
// TODO Auto-generated method stub
vehicle s=new vehicle();
s.run("汽车");
s.swim("轮船");
s.fly("飞机");
}
}
单一职责原则注意事项和细节:
1)降低类的复杂度,一个类只负责一项职责。
2)提高类的可读性,可维护性
3)降低变更引起的风险
4)通常情况下,应当遵守单一职责原则。只有逻辑足够简单,才可以在代码级违反单一职责原则: 只有类中方法数量足够少,可以在方法级别保持单一职责原则。
二.开放-封闭原则
- 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则——面向对象设计的终极目标
- 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
实例:实现一个画图形的类
本例,需要新增一个图形种类时,修改的地方比较多违反了开闭原则(对扩展(提供方)开放,对修改(使用方)关闭):需要给类增加新功能的时候, 尽量不要修改代码,或者尽可能少修改代码。
原代码:
class juxing
{
void show()
{
System.out.println("画一个矩形");
}
}
class circle
{
void show()
{
System.out.println("画一个圆形");
}
}
class sanjiaoxing//新增加
{
void show()
{
System.out.println("画一个三角形");
}
}
class GraphicDraw
{
void show(int i)
{
if(i==1)
{
juxing jx=new juxing();
jx.show();
}
else if(i==2)
{
circle c=new circle();
c.show();
}
else if(i==3)//新增
{
sanjiaoxing sjx=new sanjiaoxing();
sjx.show();
}
}
}
public class singleresponsibility {
public static void main(String[] args) {
// TODO Auto-generated method stub
GraphicDraw g=new GraphicDraw();
g.show(1);//新增之后客户端也要改
}
}
改进思路:创建抽象Shape类做成基类,并提供抽象的draw方法,让子类去实现,当需要新的图形时,只需要让新的图形继承Shape, 并实现draw方法
abstract class shape
{
public abstract void draw();
}
class GraphicDraw { //新增绘制图形不需修改此类
void drawgraph(shape s)
{
s.draw();
}
}
class juxing extends shape
{
public void draw() {
// TODO Auto-generated method stub
System.out.println("画一个矩形");
}
}
class Circle extends shape
{
public void draw()
{
System.out.println("画一个圆形");
}
}
class Triangle extends shape//新增三角形
{
public void draw()
{
System.out.println("画一个三角形");
}
}
public class singleresponsibility {
public static void main(String[] args) {
// TODO Auto-generated method stub
GraphicDraw g=new GraphicDraw();
g.drawgraph(new Circle());
g.drawgraph(new Triangle());
}
}
开闭原则:
- Open-Closed Principle原则:一个软件实体应当对扩展开放,对修改关闭。
- 开闭原则是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。
- 开闭原则中“开”,是指对于组件功能的扩展是开放的, 是允许对其进行功能扩展的;
- 开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。
三.里氏代换原则
- 继承的优点:
- 子类拥有父类的所有方法和属性,从而可以减少创建类的工作量。
- 提高了代码的重用性。
- 提高了代码的扩展性,子类不但拥有了父类的所有功能,还可以添加自己的功能。
- 继承的缺点:
- 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法。
- 降低了代码的灵活性。因为继承时,父类会对子类有一种约束。
- 增强了耦合性。当需要对父类的代码进行修改时,必须考虑到对子类产生的影响。有时修改了一点点代码都有可能需要对大段程序进行重构。
- 继承包含这样一层含义:父类中已经实现的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
缺点代码实例:
class A{
public int func1(int num1, int num2){
return num1-num2; }
}
class B extends A{
public int func1(int a,int b){
return a+b;}
public int func2(int a, int b){
return func1(a,b)+9;}
}
public class singleresponsibility {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" +a.func1(11,3));
System.out.println("1-8="+a.func1(1,8));
System.out.println ("-------");
B b = new B();
System.out.println("11-3=" + b.func1(11,3));
//这里本意是求出11-3,结果为14
System.out.println("1-8="+ b.func1(1,8));//结果为9
System.out.println("11+3+9="+b.func2(11,3));
}
}
原因就是类B重写了父类的方法func1,造成原有功能出现错误。
里氏代换原则:
- 子类型(subtype)必须能够替换它们的基(父)类型。(子类可以以父类的身份出现)
- 是关于继承机制的原则,是实现开放-封闭原则的具体规范,违反了里氏代换原则必然违反了开放-封闭原则。
- 只要有父类出现的地方,都可以使用子类来替代。而且不会出现任何错误或者异常。但是反过来却不行。子类出现的地方,不能使用父类来替代。
- 里氏代换原则的主要作用:规范继承时子类的一些书写规则。其主要目的就是保持父类方法不被覆盖。
通常的解决思路:
让原来的父类和子类都继承一个更通俗的基类, 原有的继承关系去掉,采用依赖,聚合,组合等关系代替。
class Base {
//把更加基础的方法和成员写到Base类
}
// A类
class C extends Base {
//返回两个数的差
public int func1(int num1, int num2) {
return num1 - num2; }
}
class D extends Base{
C a=new C();
public int func1(int a,int b){
return a+b; }
public int func2(int a, int b){
return func1(a,b)+9; }
public int func3(int a, int b){
return this.a.func1(a,b); }
}
public class main{
public static void main(String[] args) {
C a = new C();
System.out.println("11-3=" +a.func1(11,3));
System.out.println("1-8="+a.func1(1,8));
System.out.println ("-------");
D b= new D();
//因为D类不再继承C类,因此调用者,不会再func1是求减法
//调用完成的功能就会很明确
System. out.println("11+3=" + b. func1(11, 3));//这里本意是求出11+3
System. out.println("1+8=" + b.func1(1, 8));// 1+8
System. out.println("11+3+9=" + b.func2(11, 3));
//使用组合仍然可以使用到A类相关方法
System. out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3
}
}
- 里氏代换原则规定,子类不能覆写父类已实现的方法。父类中已实现的方法其实是一种已定好的规范和契约,如果随意修改它,那么可能会带来意想不到的错误。由子类来实现抽象方法,这里使用的其实就是里氏代换原则。
- 子类中可以增加自己特有的方法。子类继承了父类,拥有了父类和方法,同时还可以定义自己有,而父类没有的方法。这是在继承父类方法的基础上进行功能的扩展,符合里氏代换原则。
四.依赖倒转原则
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心思想是面向接口编程
- 依赖倒转原则是基于这样的设计理念:
相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类。 - 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
实例:实现person类接收信息方式
class Email
{
public String getinfo()
{
return "Email:helloworld";
}
}
/* Person 类 接受消息,将Email类作为参数 产生了依赖
1. 如果参数发生变化,即接受的是微信或短信整个方法需要改动*/
class person
{
public void person(Email e)
{
System.out.println(e.getinfo());
}
}
public class main
{
public static void main(String[] args)
{
person p=new person();
p.person(new Email());
}
}
分析:如果我们获取的对象是微信,短信等,则新增类,同时Perons也要增加相应的接收方法
解决:引入一个抽象的接口IReceiver,表示接收者,这样Person 类与接口IReceiver发生依赖
因为Emai1, WeiXin等属于接收的范围,它们各自实现IReceiver接口, 这样就符合依赖倒转原则。
修改代码
interface receive
{
public String getinfo();
}
class Email implements receive
{
public String getinfo()
{
return "Email:helloworld";
}
}
class weixin implements receive
{
@Override
public String getinfo() {
// TODO Auto-generated method stub
return "wechat:helloworld";
}
}
/* Person 类 接受消息将IReciver 接口 作为参数 产生了依赖*/
class person
{
public void person(receive e)
{
System.out.println(e.getinfo());
}
}
public class main
{
public static void main(String[] args)
{
person p=new person();
p.person(new Email());
p.person(new weixin());
}
}
依赖倒转原则的注意事项和细节:
- 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。
- 变量的声明类型尽量是抽象类或接口,这样变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。
B obj=new A(); //B是抽象类,A继承B, 如果要扩展A,则只要扩充B - 继承时遵循里氏替换原则。