java 七大设计原则之迪米特原则,开闭原则,合成复用原则
---文字代码相结合理解
七大设计原则有哪些?
- 单一职责原则
- 接口隔离原则
- 依赖倒转(倒置)原则
- 里氏替换原则
- 开闭原则
- 迪米特法则
- 合成复用原则
通常大家理解的是前六个,并没有合成复用原则
为什么要使用七大设计原则?
- 代码重用性(相同的代码不用多次编写);
- 可读性(编程的规范性,便于其他程序员的阅读和理解);
- 可拓展性(当需要添加新的功能时,非常的方便也称为可维护性);
- 可靠性(当我们添加新的功能后对原来的功能没有影响);
- 使程序呈现高内聚,低耦合等特性
迪米特原则
迪米特原则定义:
- 一个对象应该对其他对象保持最少的了解
- 类与类的关系越密切,耦合度越大
- 迪米特法则还有个更简单的定义:只与直接朋友通信
迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好,也就是说,对于被依赖的类不管多复杂,都尽量将逻辑封装在类的内部,对外提供public方法,不对外泄露信息
什么是直接朋友:
在一个类中:全局变量,返回值,参数传递就称之为直接朋友,
局部变量就称之为陌生朋友
来看一段通俗易懂的代码:
public class A {
}
public class B {
}
public class C {
}
public class D {
}
public class E {
}
public class B {
public A mA;
public C getC(){
return null;
}
public void showD(){
D d = new D();
}
public void setA(E e){
}
}
在B类中:
- public A mA; 全局变量
- public C getC(){ 返回值
return null;
} - public void showD(){ 局部变量(违反迪米特原则)
D d = new D();
} - public void setA(E e){} 参数传递
在这里A,C,E就是B的直接朋友,D就是B的陌生朋友
未遵守迪米特原则代码:
public class Student{
String name = "";
public Student(String name) {
this.name = name;
}
}
public class StudentManager {
public void getStudentNumber(Student student){
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(student.name+i);
Log.i("dimiter:","现在是第 "+i+"个学生,名字为: "+list.get(i));
}
Log.i("dimiter:","总共有 "+list.size()+"个学生 ");
}
}
//使用代码:
//迪米特原则
StudentManager studentManager = new StudentManager();
studentManager.getStudentNumber(new Student("张三"));
分析:
- 学生类(Student) 有neme参数(学生姓名)
- 学生管理类(StudentManager)创建10个学生,并输出学生名字
违反迪米特原则代码:
代码图(1.1)
:
为什么违反迪米特原则:
迪米特原则又叫做最少知道原则,首先要理解:
-
何为最少知道原则,意思就是:两个类之间耦合性特别低,耦合性特别低就代表他们之间交互特别少,即一个类对自己依赖的类知道的越少越好,在
代码图(1.1)
这个例子中可以看出,StudentManager()类对Studnet()类创建了10个学生名字.知道的太多了 -
不对外泄露信息:
代码图(1.1)
红框的代码,应该是在Student()内部完成,然后StudentManager()直接调用的
大白话解释:StudentManager()需要所有学生,正确的应该是Student()吧所有学生直接给StudentManager(),而不是给一个Student()对象,让StudentManager()自己去计算所有学生,在大白话一点就是:我问你要什么你就给我什么,不要让我来计算
(一个类对自己依赖的类知道的越少越好)
,你的东西也别让我知道(不对外泄露信息)
还是比较绕口,来看看遵守迪米特原则的代码就直接恍然大悟了~
遵守迪米特原则:
public class Student{
String name = "";///学生名字
///用来存储所有学生名字
ArrayList<String> mList = new ArrayList<>();
public Student(String name) {
this.name = name;
}
/// 遵守迪米特原则 创建10个学生
public List<String> newStudentName(){
for (int i = 0; i < 10; i++) {
mList.add(name+i);
Log.i("dimiter:","现在是第 "+i+"个学生,名字为: "+mList.get(i));
}
return mList;
}
}
public class StudentManager {
public void getStudentNumber(Student student) {
/// 遵守迪米特原则
List<String> list = student.newStudentName();
Log.i("dimiter:", "总共有 " + list.size() + "个学生 ");
}
}
//使用代码:
//迪米特原则
StudentManager studentManager = new StudentManager();
studentManager.getStudentNumber(new Student("张三"));
Student()创建10个学生,然后给到StudentManager()遵守了:
最少知道(迪米特)原则一个类对自己依赖的类知道的越少越好并且不对外泄露信息,
一个对象应该对其他对象保持最少的了解
这里的最少了解就是指10个Student()学生
我只知道你有10个学生,不管你这10个学生是怎么来的
效果图(2.1)
:
开闭原则
开闭原则定义:
- 开闭原则是编程中最基础,最重要的设计原则
- 一个软件实体如类,模块和方法应该对拓展开放(对提供方),对修改关闭(对使用方),用抽象构建框架,用实现拓展细节(细节指实现代码)
- 当软件需求要变化时,尽量通过拓展软件的实体的行为来实现变化,而不是通过修改已有的代码来实现变化
- 遵守其他原则,以及使用设计模式的目的就是遵守开闭原则
未遵守开闭原则代码:
//形状类型
public abstract class Shape {
/**
* 用来判断类型
* 1:Circular 圆形
* 2:Rectangle 矩形
*/
int type;
}
//圆形
public class Circular extends Shape {
public Circular() {
type = 1;
}
}
//矩形
public class Rectangle extends Shape {
public Rectangle() {
type = 2;
}
}
//开闭原则Manager类
public class OpenManager {
public void showShape(Shape shape){
if (shape .type == 1) {
drawCircular(shape);
}else if (shape.type == 2){
drawRectangle();
}
}
private void drawRectangle() {
Log.i("Open","创建矩形 ");
}
private void drawCircular(Shape shape) {
Log.i("Open","创建圆形 ");
}
}
//使用代码:
//开闭原则
OpenManager openManager = new OpenManager();
openManager.showShape(new Circular());//创建圆
openManager.showShape(new Rectangle());//创建矩形
效果图(2.2)
:
为什么没有遵守开闭原则:
开闭原则的最关键的定义是:
拓展开放(对提供方),对修改关闭(对使用方),用抽象构建框架,用实现拓展细节(细节指实现代码)
如果说我现在要新加一个三角形我该怎么做呢?
是这样写?
public class Triangle extends Shape{
public Triangle() {
type = 3;
}
}
public class OpenManager {
public void showShape(Shape shape){
if (shape .type == 1) {
drawCircular();//创建圆
}else if (shape.type == 2){
drawRectangle();//创建矩形
}else if(shape.type == 3){
drawTriangle();//创建三角形
}
}
private void drawTriangle() {
Log.i("Open","创建三角形 ");
}
}
//使用代码
//开闭原则
OpenManager openManager = new OpenManager();
openManager.showShape(new Circular());
openManager.showShape(new Rectangle());
openManager.showShape(new Triangle());
效果图(2.3)
:
这样写不仅在改的过程中容易导致代码的冲突,而且通过if else判断
如果我有100个图形呢?判断100次吗?
if lese if lese if lese if lese if lese if lese if lese if lese if lese if lese…?
这样写出错率太高了,而且没有遵守到拓展开放,修改关闭
遵守开闭原则代码:
public abstract class Shape {
public abstract void showShape();
}
public class Circular extends Shape {
@Override
public void showShape() {
Log.i("Open","创建圆形 ");
}
}
public class Triangle extends Shape{
@Override
public void showShape() {
Log.i("Open","创建三角形 ");
}
}
public class Rectangle extends Shape {
@Override
public void showShape() {
Log.i("Open","创建矩形 ");
}
}
public class OpenManager {
public void showShape(Shape shape){
//遵守开闭原则
shape.showShape();
}
}
//使用代码:
//开闭原则
OpenManager openManager = new OpenManager();
openManager.showShape(new Circular());
openManager.showShape(new Rectangle());
openManager.showShape(new Triangle());
分析:
- 可以看出,即使现在新增加了一个三角形类(Triangle),只要继承自Shape就可以画出来,而且不用if else来判断,代码逻辑非常清晰
- 这才是真正的对扩展开发,对修改关闭,即使我现在在增加一个椭圆形,我自需要继承自Shape()然后输出一下即可
public class Ellipse extends Shape{
@Override
public void showShape() {
Log.i("Open","我是新创建的椭圆形哦");
}
}
//开闭原则
OpenManager openManager = new OpenManager();
openManager.showShape(new Circular());
openManager.showShape(new Rectangle());
openManager.showShape(new Triangle());
openManager.showShape(new Ellipse());//创建椭圆形
和依赖倒置原则有点类似,只不过依赖倒置原则是通过接口来实现,开闭原则是通过抽象来实现.
合成复用原则
合成复用原则定义:
软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化
大白话翻译:
尽量不要继承,如果继承的话子类与父类耦合度高,父类对于子类是透明的.不利于维护,如果非要复用的话可以使用组合/聚合的方式.
不了解组合/聚合没关系,下一章我会详细介绍类与类之间的关系!
设计原则的核心思想
- 找出应用中可能需要的变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
- 面向接口编程,而不是针对实现编程。
- 为了交互对象之间的松耦合设计而努力。
- 拓展开放(对提供方),对修改关闭(对使用方),用抽象构建框架,用实现拓展细节(细节指实现代码)
设计模式总结:
- 开闭(OCP)原则: 拓展开放,修改关闭
- 接口隔离原则:类之间的依赖关系应该建立在最小的接口上
- 单一职责原则:一个类负责一项职责,而不是一个类负责一个职责
- 依赖倒置原则:面向接口编程
- 里氏替换原则:在继承时,在子类中尽量不要去重写父类的方法,如果非要使用,可以使用聚合,组合,依赖来解决问题,不要修改父类的方法!
- 迪米特(最少知道)原则:只与直接朋友通信,一个类对自己依赖的类知道的越少越好,不对外泄露信息
- 合成复用原则:要尽量使用组合或聚合的关系来实现,其次在考虑继承
猜你喜欢:
原创不易,记得点个赞哦~