设计模式之七大设计原则#
1. 单一职责原则#
1.1 代码展示及 UML 类图#
反面案例
package singlePrinciple.negative;
public class StudentManager {
public void getConnection(String url) {
System.out.println("与"+url+"建立连接");
}
public void searchStudent(String condition) {
System.out.println("根据"+condition+"查询学生信息");
}
public void deleteStudent(String condition) {
System.out.println("根据"+condition+"删除学生信息");
}
public void display() {
System.out.println("展示学生信息");
}
/**
* 1.修改连接的数据库方式,需要修改getConnection
*
*/
}
正面案列的 UML 类图
正面案列的 DBUtil.java
package singlePrinciple.positive;
//工具类
public class DBUtil {
public void getConnection(String url) {
System.out.println("与"+url+"建立连接");
}
}
Demo.java
package singlePrinciple.positive;
public class Demo {
public static void main(String[] args) {
DBUtil db=new DBUtil();
StudentDao dao=new StudentDao(db);
StudentManager sm=new StudentManager(dao);
db.getConnection("MySQL");
dao.searchStudent("孙悟空");
sm.display();
}
/**
* 1.DBUtil封装了数据库连接操作,更改数据库服务器仅仅只需要修改这个类或者配置文件
* 2.StudentDao封装了对数据库的增删改查的操作,实现增删改查比较方便
*
*/
}
然后 StudentDao.java
package singlePrinciple.positive;
public class StudentDao {
private DBUtil db;
public StudentDao(DBUtil db) {
super();
this.db = db;
}
public void searchStudent(String condition) {
System.out.println("根据"+condition+"查询学生信息");
}
public void deleteStudent(String condition) {
System.out.println("根据"+condition+"删除学生信息");
}
}
最后的 StudentManager.java
package singlePrinciple.positive;
public class StudentManager {
private StudentDao dao;
public StudentManager(StudentDao dao) {
super();
this.dao = dao;
}
public void display() {
System.out.println("展示学生信息");
}
}
1.2 单一职责原则的定义及作用(简称 SRP)#
定义:一个类仅有一个引起它变化的原因,即一个对象应该只包含一个职责。这个是最简单,最容易理解却是最不容易做到的一个设计原则,用于控制类的粒度大小。
作用:
==1. 类的复杂度降低 ==
==2. 可读性提高 ==
==3. 可维护性提高 ==
==4. 对系统的扩展性和维护性有很大的帮助 ==
==5. 单一职责原则是实现高内聚,低耦合的指导方针 ==
2. 开闭原则(即开放封闭原则)#
2.1 代码展示#
暂时无
2.2 定义及作用(简称 OCP)#
1.” 对扩展开放 “:表示实体的行为是可以扩展的。当应用的需求改变时,可以对模块进行扩展,使其具有满足那些改变的新行为
2.“对修改关闭”:表示对实体进行扩展时,不必改动模块的源代码或者二进制代码。
作用:
==(1)有利于进行单元测试;==
==(2)可以提高复用性;==
==(3)提高可维护性;==
==(4)符合面向对象开放的要求;==
3. 接口隔离原则(简称 ISP)#
3.1 代码#
反面例子,未采用接口隔离的例子。
package InterfaceIsolation.negative;
public interface HandleScore {
void update();
void insert();
void delete();
void print();
void search();
}
package InterfaceIsolation.negative;
public class Student implements HandleScore {
@Override
public void update() {
// TODO Auto-generated method stub
}
@Override
public void insert() {
// TODO Auto-generated method stub
}
@Override
public void delete() {
// TODO Auto-generated method stub
}
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println("输出学生成绩!!!!!");
}
@Override
public void search() {
// TODO Auto-generated method stub
}
/*
* 存在接口污染
* 因为学生只能查询成绩,而不能修改,记录
* 如果实现接口,除了输出成绩,其他接口用不到
*/
}
采用接口隔离原则后的案例 UML 如下:
package InterfaceIsolation.positive;
public interface Modifyable {
void delete();
void insert();
}
package InterfaceIsolation.positive;
public interface Printable {
void print();
}
package InterfaceIsolation.positive;
public interface Searchable {
void search();
}
package InterfaceIsolation.positive;
public interface Updateable {
void update();
}
package InterfaceIsolation.positive;
public class Student implements Searchable{
@Override
public void search() {
// TODO Auto-generated method stub
}
}
package InterfaceIsolation.positive;
public class Teacher implements Searchable,Updateable,Printable{
@Override
public void print() {
// TODO Auto-generated method stub
}
@Override
public void update() {
// TODO Auto-generated method stub
}
@Override
public void search() {
// TODO Auto-generated method stub
}
}
package InterfaceIsolation.positive;
public class AcademicSecretary implements Modifyable,Printable,Searchable{
@Override
public void search() {
// TODO Auto-generated method stub
}
@Override
public void print() {
// TODO Auto-generated method stub
}
@Override
public void delete() {
// TODO Auto-generated method stub
}
@Override
public void insert() {
// TODO Auto-generated method stub
}
}
3.2 定义及作用#
3.2.1 接口隔离原则:#
(1)客户端不应该依赖它不需要的接口;
(2)类之间的依赖关系应该建立在最新的接口上;
接口隔离原则是对接口的使用进行约束的一个原则
3.2.2 接口#
(1)类接口:使用 interface 定义的接口;
(2)实例接口:使用 Class 定义的类也是一种接口;
3.2.3 隔离#
==(1)客户端和它不需要的接口隔离,即客户端不使用它不需要的接口,使用该接口的客户端仅知道与之相关的方法;==
==(2)每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干,接口中没有多余的方法。==
3.2.4 作用#
(1)避免接口感染;
(2)提高灵活性;
(3)提供制定服务;
(4)实现高内聚。
4. 依赖倒置原则(简称 DIP)#
4.1 代码#
4.1.1 不使用该原则案例如下#
package Dependency.negative;
public class AutoRead {
public void read(TextRead r) {
r.read();
}
public void read(DocRead r) {
r.read();
}
public void read(ExlRead r) {
r.read();
}
public void read(XMLRead r) {
r.read();
/**
* 修改源代码,可能带来很多隐患,违反了开闭原则
*/
}
}
package Dependency.negative;
public class Demo {
public static void main(String[] args) {
AutoRead ar=new AutoRead();
ar.read(new TextRead());
ar.read(new DocRead());
ar.read(new ExlRead());
}
/**
* 如果此时要增加读取xml文件的功能,那就需要新建xlm读取功能类,然后再AutoRead方法中增加read此功能,这样违反了开闭原则
*
*/
}
package Dependency.negative;
public class DocRead {
public void read() {
System.out.println("读取doc文件!!!");
}
}
package Dependency.negative;
public class ExlRead {
public void read() {
System.out.println("读取Excel文件!!");
}
}
package Dependency.negative;
public class TextRead {
public void read() {
System.out.println("读取text文件!");
}
}
package Dependency.negative;
public class XMLRead {
public void read() {
System.out.println("读取xml文件");
}
}
4.1.2 使用原则案例如下:#
package Dependency.positive;
public interface Readable {
void read();
}
package Dependency.positive;
public class AutoRead{
public void read(Readable r) {
r.read();
}
}
package Dependency.positive;
public class Demo {
public static void main(String[] args) {
AutoRead ar=new AutoRead();
ar.read(new DocRead());
ar.read(new ExlRead());
ar.read(new TxtRead());
ar.read(new XMLRead());
}
}
package Dependency.positive;
public class DocRead implements Readable{
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("读取doc文件");
}
}
package Dependency.positive;
public class ExlRead implements Readable{
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("读取xls文件。");
}
}
package Dependency.positive;
public class TxtRead implements Readable{
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("读取txt文件。");
}
}
package Dependency.positive;
public class XMLRead implements Readable{
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("读取xml文件");
}
}
4.2 定义及作用#
4.2.1 定义:#
高层模块不应该依赖低层模块,他们都应该依赖抽象,抽象不应该依赖细节,细节应该依赖于抽象
本质就是通过抽象(接口或者抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块机间的松耦合。
4.2.2 作用:#
==(1)减少类间的耦合性;==
==(2)提高系统的稳定,降低并发开放引起的风险;==
==(3)提高代码的可读性和可维护性;==
5. 里氏替换原则(简称 LSP)#
5.1 代码#
5.1.1 反例代码:#
package substitution.negative;
public class ReadDoc extends ReadFile{
@Override
public void read(String fileName) {
// TODO Auto-generated method stub
System.out.println("读取doc文件:"+fileName);
}
}
package substitution.negative;
public class ReadFile {
public void read(String fileName) {
System.out.println("读取excel文件"+fileName);
}
}
package substitution.negative;
public class Demo {
public static void readFile(ReadFile rf,String fileName) {
rf.read(fileName);
}
public static void main(String[] args) {
readFile(new ReadFile(),"a.xsl");
readFile(new ReadDoc(),"b.xsl");
}
}
5.1.2 正面案例:#
UML 如下:
package substitution.positive;
public interface Readable {
void read(String fileName);
}
package substitution.positive;
public class ReadDoc implements Readable{
@Override
public void read(String fileName) {
// TODO Auto-generated method stub
System.out.println("读取doc文件:"+fileName);
}
}
package substitution.positive;
public class ReadXsl implements Readable{
@Override
public void read(String fileName) {
// TODO Auto-generated method stub
System.out.println("读取Excel文件:"+fileName);
}
}
package substitution.positive;
/**
*
* @author ASUS
*继承有优点也有缺点
*继承是侵入性的,增加了耦合
*/
public class Demo {
public static void readFile(Readable rf,String fileName) {
rf.read(fileName);
}
public static void main(String[] args) {
Readable r=new ReadDoc();
readFile(r,"a.doc");
ReadXsl rs=new ReadXsl();
readFile(rs,"b.xsl");
}
}
5.2 定义及作用#
5.2.1 定义#
所有引用基类的地方必须能透明地使用其子类对象,即替换成子类之后,系统的行为不会发生变化。
里氏替换原则定义了继承关系要遵守的 4 个规则:
==(1) 子类可以实现父类的抽象方法,但不能覆写父类的非抽象方法;==
==(2) 子类中可以增加自己特有的方法,体现子类的个性;==
==(3) 当子类的方法实现父类的方法时 (重写 / 重载或实现抽象方法) 时,方法的前置条件 (即方法的输入参数) 要比父类方法的输入参数更宽松或相等;==
==(4) 当子类的方法实现父类的方法时 (重写 / 重载或实现抽象方法),方法的后置条件 (即方法的输出 / 返回值) 要比父类更严格或相等。==
5.2.2 作用#
(1)约束继承泛滥,开闭原则的一种体现;
(2)加强程序的健壮性,在程序变化可以做到非常好的兼容性,提高程序的维护性,扩展性;
(3)降低需求变更引入的风险。
6. 组合复用原则(简称 CRP)#
6.1 代码#
反面:
package combined.negative;
class Person{
}
class Teacher extends Person{
}
class Student extends Person{
}
class PrimarySchoolTeacher extends Teacher{
}
class JuniorSchoolTeacher extends Teacher{
}
class HighSchoolTeacher extends Teacher{
}
class PrimaryStudent extends Student{
public void say() {
System.out.println("我是小学生");
}
}
class JuniorStudent extends Student{
public void say() {
System.out.println("我是初中生");
}
}
class HighStudent extends Student{
public void say() {
System.out.println("我是高中生");
}
}
class Logistics extends Person{
}
class PrimaryLigistics extends Logistics{
}
class JuniorLigistics extends Logistics{
}
class HighLigistics extends Logistics{
}
public class Demo {
public static void main(String[] args) {
}
}
正面:
package combined.positive;
abstract class Person{
private String name;
private SchoolLevel level;
public Person(String name, SchoolLevel level) {
super();
this.name = name;
this.level = level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLevel() {
return this.level.getLevel();
}
public void setLevel(SchoolLevel level) {
this.level = level;
}
}
class Teacher extends Person{
public Teacher(String name, SchoolLevel level) {
super(name, level);
// TODO Auto-generated constructor stub
}
public void say() {
System.out.println(super.getName()+"是"+super.getLevel()+"老师");
}
}
class Student extends Person{
public Student(String name, SchoolLevel level) {
super(name, level);
// TODO Auto-generated constructor stub
}
public void say() {
System.out.println(super.getName()+"是"+super.getLevel()+"学生");
}
}
interface SchoolLevel{
String getLevel();
}
class PrimaryLevel implements SchoolLevel{
@Override
public String getLevel() {
// TODO Auto-generated method stub
return "小学";
}
}
class JuniorLevel implements SchoolLevel{
@Override
public String getLevel() {
// TODO Auto-generated method stub
return "初中";
}
}
class HighLevel implements SchoolLevel{
@Override
public String getLevel() {
// TODO Auto-generated method stub
return "高中";
}
}
class Logistics extends Person{
public Logistics(String name, SchoolLevel level) {
super(name, level);
// TODO Auto-generated constructor stub
}
public void say() {
System.out.println(super.getName()+"是"+super.getLevel()+"后勤管理人员");
}
}
public class Demo {
public static void main(String[] args) {
SchoolLevel level=new HighLevel();
Student swk=new Student("孙悟空",level);
swk.say();
Teacher puti=new Teacher("扶梯老祖",new PrimaryLevel());
puti.say();
Logistics cattle=new Logistics("牛魔王",new JuniorLevel());
cattle.say();
}
}
6.2 定义及作用#
6.2.1 定义#
优先使用对象组合(聚合),而不是继承来达到复用目的。
在一个新的对象里通过关联关系(包括组合和聚合关系) 来使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用功能的目的。
即尽量使用组合(contains-a) 和聚合 (has-a), 而不是继承(is-a)达到复用的目的
6.2.2 作用#
(1)实现简单。通过继承,子类拥有父类的功能;
(2)易于扩展。子类通过增加新的属性和方法对父类进行扩展。
组合复用原则的作用
(1) 新对象存取成员对象的唯一途径是通过成员对象的接口,这种复用是黑箱复用,使系统更加灵活,降低类与类之间的耦合度;
(2) 成员对象的内部细节对新对象是不可见的,这种复用支持封装,减少依赖,一个类的变化对其他类造成的影响相对较少;
(3) 每一个新的类可以将焦点集中在一个任务上;
(4) 这种复用可以在运行时动态进行,新对象可动态引用与成员对象类型相同的对象。
不足之处:== 这种方式建造的系统会有较多的对象需要管理。==
7. 迪米特法则(LOD)#
不使用迪米特法则的例子
package lkp.negative;
public class ExcelToDB {
private String fileName;
private String db;
public ExcelToDB(String fileName, String db) {
super();
this.fileName = fileName;
this.db = db;
}
public String read() {
return this.fileName+"文件内容!";
}
public void connectionDB() {
System.out.println("连接"+this.db+"数据库系统");
}
public void save(String conten) {
System.out.println("把"+this.read()+"保存在数据库中!!");
}
}
客户端代码(客户端暴露的代码太多)
package lkp.negative;
//迪米特法则的反例
public class Client {
public static void main(String[] args) {
ExcelToDB todb=new ExcelToDB("a.xsl","MySQL");
String str=todb.read();
todb.connectionDB();
todb.save(str);
}
}
迪米特法则的优点,== 客户端不需要知道如何转换,仅仅调用工具类的转换方法就能完成信息转换过程,客户端用最少知识完成了任务。==
迪米特法则:一个对象应当对其他的对象尽可能少的了解,不和陌生人说话,最少知识原则(==LKP==)。
尽可能减少对象之间的交互,如果一个对象需要调用另一个对象的方法,可以通过第三者调用。
朋友圈的成员:
==1. 当前对象本身(this)==
==2. 当前对象方法中的参数 ==
==3. 当前对象的实例变量之间引用的对象 ==
==4. 当前对象的实例变量如果是一个聚集,那么聚集的元素也是朋友 ==
==5. 当前对象方法所创建的对象 ==
四个要求:
== 优先考虑将一个类设置成不变类 ==
== 尽量降低一个类的访问权限 ==
== 谨慎使用 Serializable==
== 尽量降低成员的访问权限 ==
package lkp.positive;
public class ExcelToDB {
private String fileName;
private String db;
public ExcelToDB(String fileName, String db) {
super();
this.fileName = fileName;
this.db = db;
}
//私有化方法,避免暴露
private String read() {
return this.fileName+"文件内容!";
}
private void connectionDB() {
System.out.println("连接"+this.db+"数据库系统");
}
private void save(String conten) {
System.out.println("把"+this.read()+"保存在数据库中!!");
}
public void perform() {
String str=this.read();
this.connectionDB();
this.save(str);
}
}
中介者如下
package lkp.positive;
public class UtilConvert {
private ExcelToDB todb;
public UtilConvert(ExcelToDB todb) {
super();
this.todb = todb;
}
public void convert() {
todb.perform();
}
}
客户端
package lkp.positive;
public class Client {
public static void main(String[] args) {
ExcelToDB todb=new ExcelToDB("a.xsl","MYSQL");
UtilConvert convert=new UtilConvert(todb);
convert.convert();
}
}