设计模式—— 七大设计原则
Posted 玛丽莲茼蒿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式—— 七大设计原则相关的知识,希望对你有一定的参考价值。
无论是7个设计原则还是设计模式,完全遵循是很难的,但应有意识尽量遵循。
不要对设计模式或者设计原则感到陌生和害怕,其实在正式前,我们已经在“偷偷”使用了。比如我们的setter方法和getter方法。
一个类明明可以把成员变量定义成public,然后对象p直接p.age这样使用
class People
puclic int age;
但是良好的编程习惯告诉我们要让成员变量私有化,只对外提供setter和getter的public接口。你可以把它理解成“最少知道原则”
class People
private int age;
public setAge()...
public getAge()...
设计模式都是遵循以下7个原则去设计的:
- 单一职责原则
- 接口隔离原则
- 依赖倒置原则(面向接口编程原则)
- 里式替换原则
- 开闭原则
- 迪米特法则
- 合成复用原则
一、单一职责原则
定义:在类的级别上,一个类只负责一项职责;在方法的级别上,一个方法只做一件事。
二、接口隔离原则
定义:一个类对另一个类的依赖应该建立在最小接口上。
举个违反接口隔离原则的例子:
类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D(看了代码就明白了)。
package HeadFirst;
/**
* 【违反】接口隔离原则的例子
*/
public class InterfaceIsolationTest
public static void main(String[] args)
//
B b = new B();
A a = new A();
a.test1(b);
a.test2(b);
a.test3(b);
//
D d = new D();
C c = new C();
c.test1(d);
c.test4(d);
c.test5(d);
interface Interface1
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
//类B实现接口Interface1
class B implements Interface1
public void operation1()
System.out.println("B 实现operation1");
public void operation2()
System.out.println("B 实现operation2");
public void operation3()
System.out.println("B 实现operation3");
public void operation4()
System.out.println("B 实现operation4");
public void operation5()
System.out.println("B 实现operation5");
//类D实现接口Interface1
class D implements Interface1
public void operation1()
System.out.println("D 实现operation1");
public void operation2()
System.out.println("D 实现operation2");
public void operation3()
System.out.println("D 实现operation3");
public void operation4()
System.out.println("D 实现operation4");
public void operation5()
System.out.println("D 实现operation5");
//类A 通过接口依赖类B
class A
public void dependB1(Interface1 i)
i.operation1(); //B的operation1
public void dependB2(Interface1 i)
i.operation2();//B的operation2
public void dependB3(Interface1 i)
i.operation3();//B的operation3
//类C 通过接口依赖类D
class C
public void dependD1(Interface1 i)
i.operation1();//D的operation1
public void dependD4(Interface1 i)
i.operation4();//D的operation4
public void dependD5(Interface1 i)
i.operation4();//D的operation5
类A只需要使用类B对接口的1/2/3个实现,但是B却实现了接口全部方法;同样,类C只需要使用类D对接口的1/4/5个实现,但是D也实现了接口的全部方法。对B来说,方法4和5是完全没必要实现的,对D来说方法2和3是完全没必要实现的。
按照【接口隔离】原则,划分出3个【最小接口】:
三、依赖倒置原则
定义:
做法/核心思想:面向接口编程,而不是面向具体的实现类编程
3.1 例子
这是之前就体会到也是【最常见】的一种原则 。
什么是依赖?Person类在其方法中使用了另一个类——Email类,就是Person类依赖于Email类。
如果Person类还要接收微信消息、QQ消息,就要对receive方法进行重载。所以面向接口编程就是为了解决大量重载的问题,缩短代码量。
解决:
3.2 依赖倒置的三种使用方法/依赖传递的3种方法
还是先弄清被传递的【依赖】是谁?在下面这个例子中,DriverOne类中使用了车的接口ICar,也就是司机依赖于车,所以被传递的依赖是【车】
1. 接口传递
package HeadFirst;
public class Priciple
public static void main(String[] args)
NISSAN nissan = new NISSAN();
DriverOne driver = new DriverOne();
driver.drive(nissan); //通过接口传递依赖
interface ICar
public void run();
interface IDriver
public void drive(ICar car); //接口IDriver 依赖于 接口ICar
class DriverOne implements IDriver
@Override
public void drive(ICar car)
car.run();
class NISSAN implements ICar
@Override
public void run()
System.out.println("尼桑在跑了");
2. 构造方法传递
构造方法传递和setter方法的思路是一样的。直接把依赖的【车】定义为自己的成员变量。
package HeadFirst;
public class Priciple
public static void main(String[] args)
NISSAN nissan = new NISSAN();
DriverOne driver = new DriverOne(nissan);//构造方法传递依赖
driver.drive();
interface ICar
public void run();
interface IDriver
public void drive();
class DriverOne implements IDriver
ICar car; //把依赖的车直接定义为成员变量
DriverOne(ICar car)
this.car = car; //构造方法里传递依赖
@Override
public void drive()
car.run();
class NISSAN implements ICar
@Override
public void run()
System.out.println("尼桑在跑了");
3. setter方法传递
直接把依赖的【车】定义为自己的成员变量,但不在构造方法里传递依赖,而是再写一个专门传递依赖的setter方法。
package HeadFirst;
public class Priciple
public static void main(String[] args)
NISSAN nissan = new NISSAN();
DriverOne driver = new DriverOne();//构造方法传递依赖
driver.setCar(nissan);
driver.drive();
interface ICar
public void run();
interface IDriver
public void drive();
class DriverOne implements IDriver
ICar car; //把依赖的车直接定义为成员变量
@Override
public void drive()
car.run();
public void setCar(ICar car) //setter传递依赖
this.car = car;
class NISSAN implements ICar
@Override
public void run()
System.out.println("尼桑在跑了");
四、里式替换原则
继承带来的弊端:如果A是祖宗类,B1继承A,B2继承A,C继承B1,D继承C,E继承D......那么一旦我们想要对A发生修改,就要考虑上述所有类。
那么如何正确使用继承?里式替换原则!
定义:在子类中不要重写父类的方法(重写抽象类的抽象方法除外)。通过极限思想来考虑,如果子类重写了父类的所有方法,那子类还继承父类干嘛呢?直接自己造一个类不就好了。
做法:如果B想要继承A并重写A的方法,那再定义一个更高层的类Base,让B和A都继承Base,此时A和B不是父子关系,而是同等地位,现在B想要用A中的东西,就可以聚合、组合、依赖的方式。
五、开闭原则
定义:对扩展开放,对修改关闭。(对提供类开放,对使用类关闭。比如司机和车,司机是车的使用者,那么司机就是使用类,车是提供类)
做法:如果要增加软件的功能,可以自己添加新的类,但不要动已经写好的代码。
这是7个原则中最核心也是最常用的原则,通俗来说,就是当你进入到一个新团队,无论前面的人写的代码多“垃圾”,都不要动。
其实开闭原则我们在之前依赖倒置的例子中“偷偷”用过。
开闭原则使用前后,UML图是不变的,我们直接看代码。
public class Priciple
public static void main(String[] args)
Driver driver = new Driver();
driver.driveCar(new HongQiCar());
driver.driveCar(new BenChiCar());
class Driver
public void driveCar(Car car)
if(car.CarType == 1)
HongQiRun();
else if(car.CarType==2)
BenChiRun();
void HongQiRun()
System.out.println("红旗汽车在跑了");
void BenChiRun()
System.out.println("奔驰汽车在跑了");
class Car
public int CarType;
class HongQiCar extends Car
HongQiCar()CarType=1;
class BenChiCar extends Car
BenChiCar()CarType=2;
如果软件需求变更,要临时增加一辆新的车“尼桑”,那我们就要这样修改:
首先新增一个尼桑车类,这是无可厚非的(对扩展开放)。
class NissanCar extends Car
NissanCar()CarType=3;
然后要将使用类进行如下修改:
而这个使用类Driver是前人写好的,根据开闭原则,是不应该再打开它进行我们的修改的。
正确写法:
public class Priciple
public static void main(String[] args)
Driver driver = new Driver();
driver.driveCar(new HongQiCar());
driver.driveCar(new BenChiCar());
class Driver
public void driveCar(Car car)
car.run();
abstract class Car
public int CarType;
abstract public void run();
class HongQiCar extends Car
@Override
public void run()
System.out.println("红旗汽车在跑了");
class BenChiCar extends Car
@Override
public void run()
System.out.println("奔驰汽车在跑了");
这个时候,我们需要新加一辆尼桑汽车时,只需要添加一个类就好了:
class NissanCar extends Car
@Override
public void run()
System.out.println("尼桑汽车在跑了");
六、迪米特法则/最少知道原则
定义:每个类A都避免不了与其他类B产生关系,但我们让这种关系越小越好。即类A对类B的内部了解的越少越好,如果类A要用到类B,最好只需要调用类B提供的public方法即可。
做法:只允许在一个类的成员变量(组合)、方法参数(依赖)、方法返回值处使用另一个类的对象。不允许把另一个类的对象当做自己的局部变量。
下面的例子是SchoolManager类的内部:
如果想要写出“蓝色选中”部分的代码, SchoolManager类需要了解CollegeManger内部的逻辑才行,这也就违背了最少知道原则。正确做法是把这一部分逻辑拿到CollegeManger里去写,然后只提供给SchoolManager类一个public接口去调用即可。
七、合成复用原则
定义:能使用组合、依赖、聚合就不使用继承
以上是关于设计模式—— 七大设计原则的主要内容,如果未能解决你的问题,请参考以下文章