设计原则:
在软件设计中应该遵循的原则,这样比较容易设计出易扩展、易维护、可重用、灵活性高的软件架构。设计原则是设计模式的基础,每个设计模式或多或少都会遵循一个以上的设计原则。
单一职责原则:
对一个类或者方法来说,只负责一项职责,如果一个类负责多个职责的话,某个职责的修改可能会导致令一个职责错误,所以要将职责分离开来。可以在类级别或者方法级别上遵守单一职责原则。
UML图及代码:
改进前:

/**
* 交通工具
*/
public class Vehicle {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
System.out.println(vehicle.run("汽车"));
System.out.println(vehicle.run("火车"));
System.out.println(vehicle.run("飞机"));
}
public String run(String name){
return name + "在公路上行驶";
}
}
//飞机在公路上行驶明显是不符合逻辑的,该代码就没有遵循单一职责原则,因为在天空中的交通工具跟在公路上的交通工具的职责是不一样的,所以要分离开来。
改进后:

public class RoadVehicle {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
SkyVehicle skyVehicle = new SkyVehicle();
System.out.println(roadVehicle.run("汽车"));
System.out.println(roadVehicle.run("火车"));
System.out.println(skyVehicle.run("飞机"));
}
public String run(String name){
return name + "在公路上行驶";
}
}
public class SkyVehicle {
public String run(String name){
return name + "在天空中上飞行";
}
}
//把职责分离开来,天空的交通工具只负责天空飞行的职责,公路上的交通工具只负责在公路上行驶的职责。
单一职责的优点和细节:
- 可以降低类的复杂度,因为一个类只负责一项职责,类也相对简单了。
- 提高了维护性和可读性。
- 降低变更引起的风险。
- 通常情况下,我们应该遵守单一职责原则,只有在代码逻辑足够简单的情况下才能违反单一职责原则。只有类中的方法足够少,才能在方法层面违反该原则。
接口隔离原则:
客户端不应该依赖于他不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。例如:A依赖于B,B实现了接口C,A类就要用到C接口的所有方法。
uml图及代码:

public interface A {
void operator1();
void operator2();
void operator3();
void operator4();
void operator5();
}
public class B implements A {
@Override
public void operator1() {
System.out.println("B实现A-->operator1()");
}
@Override
public void operator2() {
System.out.println("B实现A-->operator2()");
}
@Override
public void operator3() {
System.out.println("B实现A-->operator3()");
}
@Override
public void operator4() {
System.out.println("B实现A-->operator4()");
}
@Override
public void operator5() {
System.out.println("B实现A-->operator5()");
}
}
public class C implements A {
@Override
public void operator1() {
System.out.println("C实现A-->operator1()");
}
@Override
public void operator2() {
System.out.println("C实现A-->operator2()");
}
@Override
public void operator3() {
System.out.println("C实现A-->operator3()");
}
@Override
public void operator4() {
System.out.println("C实现A-->operator4()");
}
@Override
public void operator5() {
System.out.println("C实现A-->operator5()");
}
}
public class D {
public void useOperator1(C c){
c.operator1();
}
public void useOperator2(C c){
c.operator2();
}
public void useOperator3(C c){
c.operator3();
}
}
public class E {
public void useOperator1(B b){
b.operator1();
}
public void useOperator4(B b){
b.operator4();
}
public void useOperator5(B b){
b.operator5();
}
}
//这个是违反了接口隔离原则的,因为类D依赖了类C却只使用了接口A的其中三个方法而已,类E依赖了类B却只使用了接口A的其中三个方法而已。
改进后:

public interface A {
void operator1();
}
public interface B {
void operator2();
void operator3();
}
public interface C {
void operator4();
void operator5();
}
public class D implements A,B {
@Override
public void operator1() {
System.out.println("D 实现 A-->operator1()");
}
@Override
public void operator2() {
System.out.println("D 实现 B-->operator2()");
}
@Override
public void operator3() {
System.out.println("D 实现 B-->operator3()");
}
}
public class E implements A,C{
@Override
public void operator1() {
System.out.println("E 实现 A-->operator1()");
}
@Override
public void operator4() {
System.out.println("E 实现 C-->operator4()");
}
@Override
public void operator5() {
System.out.println("E 实现 C-->operator5()");
}
}
public class F {
public void useOperator1(D d){
d.operator1();
}
public void useOperator2(D d){
d.operator2();
}
public void useOperator3(D d){
d.operator3();
}
}
public class G {
public void useOperator1(E e){
e.operator1();
}
public void useOperator4(E e){
e.operator4();
}
public void useOperator5(E e){
e.operator5();
}
}
//将本来的接口A拆分成了A、B、C ,然后D类实现接口A、B,E类实现接口A、C,这样就达到了接口隔离的原则。
依赖倒置原则:
- 高层模块(调用方)不应该依赖低层模块(提供方),二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。依赖传递都传递抽象,接口或者抽象类。
- 依赖倒置的中心思想是面向接口编程。充分利用多态。
- 依赖倒置原则基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多,以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定得多,在java中,抽象指的是接口或抽象类,细节就是具体的实现类。
- 使用接口或者抽象类的目的是制定好规范,而不涉及任何具体操作,把展现细节的任务交给他们的实现类去完成。
依赖传递的三种方法:
- 接口。
- setter方法。
- 构造器。
UML图及代码:
改进前:

public class RoadVehicle {
private String name;
public void setName(String name){
this.name = name;
}
public void run(){
System.out.println(name + "在公路上行驶");
}
}
public class SkyVehicle {
private String name;
public void setName(String name){
this.name = name;
}
public void run(){
System.out.println(name + "在天空中飞行");
}
}
public class TestVehicle {
public void runRoadVehicle(RoadVehicle roadVehicle){
roadVehicle.run();
}
public void runSkyVehicle(SkyVehicle skyVehicle){
skyVehicle.run();
}
}
//分析:TestVehicle 类是高层模块(调用方),依赖了SkyVehicle、RoadVehicle底层模块,而没有依赖其抽象,违反了依赖倒置原则,所以如果每次增加一个新的交通工具TestVehicle 类就要新增一个方法。
改进后:

public interface Vehicle {
void run();
}
public class RoadVehicle implements Vehicle{
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public void run(){
System.out.println(name + "在公路上行驶");
}
}
public class SkyVehicle implements Vehicle{
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public void run(){
System.out.println(name + "在天空中飞行");
}
}
public class TestVehicle {
public void runVehicle(Vehicle Vehicle){
Vehicle.run();
}
public static void main(String[] args) {
TestVehicle testVehicle = new TestVehicle();
testVehicle.runVehicle(new RoadVehicle());
testVehicle.runVehicle(new SkyVehicle());
}
}
//分析:改进后,RoadVehicle 、SkyVehicle 会实现Vehicle接口,然后TestVehicle 没有依赖低层模块,而是依赖其抽象,这样,每当新增一个交通工具时,高层接口无需改变。
依赖倒置原则的注意事项和细节:
- 底层模块尽量都要有抽象父类或接口。或者两者都有。
- 变量的声明一般都用抽象父类或者接口,然后将引用指向实际的实现类(多态),这样引用变量和时间对象之间会有一个缓冲层,利于程序扩展和优化。
- 继承时遵循里氏替换原则。
里氏替换原则:
面向对象中的继承性的思考和说明:
- 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然他不强制要求所有子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
- 继承在给程序设计带来便利的同时,也会带来弊端,比如继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他类所继承,则当这个类需要修改时,必须考虑到所有子类,并且父类修改后,所有涉及到子类的功能都有可能故障。
- 而里氏替换原则是对继承的规范和约束。
里氏替换原则定义:有类A和B,B继承A,OA是A的对象,OB是B的对象,一个程序P调用OA对象的所有方法,如果换成调用OB对象的这些方法的话,对这个程序P的行为没有任何影响,则符合里氏替换原则。也就是说子类不应该重写父类已实现的方法,调用父类的方法与调用子类的相应方法所产生的行为一致。
UML图及代码:
改进前:

public class A {
/**
* 加法
* @param member1
* @param member2
* @return
*/
public int fun(int member1,int member2){
return member1 + member2;
}
}
public class B extends A{
/**
* 减法
* @param member1
* @param member2
* @return
*/
@Override
public int fun(int member1,int member2){
return member1 - member2;
}
}
public class TestAB {
public static void main(String[] args) {
A a = new A();
A b = new B();
System.out.println(a.fun(1,2));
System.out.println(b.fun(1,2));
}
}
//分析,由于子类重写了父类已经实现的方法,所以调用这两个会出现不一样的结果,这无疑会使系统更加地复杂。
改进后:

public abstract class Base {
public abstract int fun(int member1,int member2);
}
public class A extends Base{
/**
* 加法
* @param member1
* @param member2
* @return
*/
@Override
public int fun(int member1,int member2){
return member1 + member2;
}
}
public class B extends Base {
/**
* 减法
* @param member1
* @param member2
* @return
*/
@Override
public int fun(int member1,int member2){
return member1 - member2;
}
}
public class TestAB {
public static void main(String[] args) {
A a = new A();
B b = new B();
System.out.println(a.fun(1,2));
System.out.println(b.fun(1,2));
}
}
//分析:使用了更抽象的类base,然后子类各自实现其抽象方法,使得程序更加地清晰。
在使用继承时,尽量遵循里氏替换原则,不要重写已实现父类的方法。
里氏替换原则告诉我们,继承实际上让两个类的耦合性增强了,在适当情况下可以通过聚合、组合、依赖来解决问题。
通常做法:原来的父类和子类继承同一个更抽象的基类,原有的继承去掉,采用依赖、聚合、组合。等关系代替。
开闭原则:
开闭原则:
- 是编程中最基础,最重要的设计原则。是其他原则的基础。其他原则都能看到这个原则的身影。
- 一个软件实体,如类,模块和函数应该对扩展开放(提供方),对修改关闭(使用方),用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化(比如新增类),而不是修改已有的代码来实现。
- 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
代码跟依赖倒置原则类似。
迪米特原则:
- 一个对象应该和其他对象保持最小的了解。
- 类与类之间的关系越密切,耦合度越大。
- 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道地越少越好。也就是说对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部,对外除了提供public方法之外,不对外泄露任何信息。
- 迪米特法则还有个更简单的定义:只与直接朋友通信。
直接朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式有很多:依赖、关联、组合、聚合等。其中:我们称出现在成员变量、方法参数、方法返回值的类为直接的朋友,而出现在局部变量的类不是直接朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
就是别的类的逻辑信息不要出现在令一个类。而是完美的封装在原来的类里面,只暴露public方法供调用。
迪米特法则的注意事项和细节:
- 迪米特法则的核心是降低类之间的耦合。
- 由于每个类都减少了不必要的依赖,所以迪米特法则只是要求降低类(对象间的耦合关系),并不是完全消除耦合。
合成复用原则:
原则是尽量使用合成/聚合/组合的方式代替继承。
设计原则的核心思想:
- 找出应用中可能需要变化之处,把它们独立起来,不要和那些不需要变化的代码混在一起。
- 针对接口编程而不是针对实现编程。
- 为交互对象之间的松耦合而努力。
1501

被折叠的 条评论
为什么被折叠?



