设计模式之常用法则

设计模式

软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

设计模式的本质:

面向对象设计原则的实际运用,是对类的封装,继承,多态以及类的关联关系和组合关系的充分理解。

何时会用到设计模式

设计模式的重要性:

对软件设计中普遍存在(重复出现)的问题所提出的一系列的解决问题的方法。
设计模式在软件中的哪里?
面向对象=>功能模块[设计模式+算法[数据结构]]=>框架[多种设计模式]=>架构[服务群集群技术]

采用设计模式的目的

在编写代码的过程中,程序员会面临耦合性,内聚性,可维护性,可扩展性,灵活性,重用性等一系列多方面的要求,设计模式是为了使程序有更好的可读性,代码重用性,可维护和课扩展性,也可以使程序具有高内聚,低耦合的特性。

设计模式的七大原则

七大原则核心思想

1、单一职责原则
概念

一个类只负责一个职责,如果违背单一职责原则,建议使用分类或者添加方法来实现单一职责原则。

案例分析

违反单一职责原则案例

package com.tedu.principle.single;
//单一职责原则
public class SingleResponsitity1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        vehicle.run("飞机");
    }
}

//例子
class Vehicle{
    /**
     * 在该方法中违反了单一职责原则,
     * 解决方法:分类即可
     * @param vehicle
     */
    public void run(String vehicle){
        System.out.println(vehicle + "在公路上跑");
    }
}

解决方案1

package com.tedu.principle.single;

public class SingleResponsitity2 {
    public static void main(String[] args) {
        RoadVehicle vehicle = new RoadVehicle();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        AirVehicle vehicle1 = new AirVehicle();
        vehicle1.run("飞机");
    }
}
//例子
/**
 * 遵守了单一职责原则,改动很大,成本太大
 */
class RoadVehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在公路上运行");
    }
}
//例子
class AirVehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在天空上运行");
    }
}
//例子
class WaterVehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在水上运行");
    }
}

解决方案2

package com.tedu.principle.single;

public class SingleResponsitity3 {
    public static void main(String[] args) {
        Vehicle2 vehicle = new Vehicle2();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        vehicle.runAir("飞机");
    }
}
//例子
class Vehicle2{
    /**
     * 这种修改方法没有对原来的类做大的修改,只是增加了方法,
     * 在类上违反了单一职责原则(同类不存在),但是方法上实现了单一原则(不算违反单一职责原则)
     * @param vehicle
     */
    public void run(String vehicle){
        System.out.println(vehicle + "在公路上跑");
    }
    public void runAir(String vehicle){
        System.out.println(vehicle + "在天空上跑");
    }
    public void runWater(String vehicle){
        System.out.println(vehicle + "在水上跑");
    }
}
注意事项和细节
  1. 降低类的复杂度,一个类只负责一个职责
  2. 提高类的可读性和可维护性
  3. 降低变更引起的风险
  4. 我们应当遵守单一职责原则,只是逻辑足够简单,才可以在代码级违反单一职责原则,如果类中的方法足够简单,可以在方法上保持单一职责原则
2、接口隔离原则(Interface Segregation Princple)
概念

客户端不应该依赖他不需要的接口,一个类对另一个类的依赖,建立在最小的接口上.
**用法:**将接口拆分成几个独立的几个接口,不同的类分别与他们需要的接口建立依赖关系

案列分析

在这里插入图片描述
Java实现代码

package com.tedu.principle.segregation;

public class Segregation1 {
    public static void main(String[] args) {

    }
}
//接口
interface Segregation01{
    void operation01();
    void operation02();
    void operation03();
    void operation04();
    void operation05();
}
class B implements Segregation01{

    @Override
    public void operation01() {
        System.out.println("B实现了operation方法1");
    }

    @Override
    public void operation02() {
        System.out.println("B实现了operation方法2");
    }

    @Override
    public void operation03() {
        System.out.println("B实现了operation方法3");
    }

    @Override
    public void operation04() {
        System.out.println("B实现了operation方法4");
    }

    @Override
    public void operation05() {
        System.out.println("B实现了operation方法5");
    }
}
class D implements Segregation01{

    @Override
    public void operation01() {
        System.out.println("D实现了operation方法1");
    }

    @Override
    public void operation02() {
        System.out.println("D实现了operation方法2");
    }

    @Override
    public void operation03() {
        System.out.println("D实现了operation方法3");
    }

    @Override
    public void operation04() {
        System.out.println("D实现了operation方法4");
    }

    @Override
    public void operation05() {
        System.out.println("D实现了operation方法5");
    }
}
class A { //A通过依赖的方式使用了Segregation01中的方法1,2,3
    public void depend01(Segregation01 seg){
        seg.operation01();
    }
    public void depend02(Segregation01 seg){
        seg.operation02();
    }
    public void depend03(Segregation01 seg){
        seg.operation03();
    }
}
class C { //A通过依赖的方式使用了Segregation01中的方法1,4,5
    public void depend01(Segregation01 seg){
        seg.operation01();
    }
    public void depend04(Segregation01 seg){
        seg.operation04();
    }
    public void depend05(Segregation01 seg){
        seg.operation05();
    }
}

以上代码存在代码重复和代码冗余的问题,修改代码如下:
在这里插入图片描述

package com.tedu.principle.segregation;

public class Segregation2 {
    public static void main(String[] args) {
        A1 a = new A1();
        a.depend01(new B1());
        a.depend02(new B1());
        a.depend03(new B1());
        C1 c = new C1();
        c.depend01(new D1());
        c.depend04(new D1());
        c.depend05(new D1());
    }
}
interface Segregation02{
    void operation01();
}

/**
 * interface1和interface2是
 */
interface Interface1 {
    void operation02();
    void operation03();
}
interface Interface2{
    void operation04();
    void operation05();
}
class B1 implements Segregation02,Interface1{

    @Override
    public void operation01() {
        System.out.println("B1实现了Segregation01中的operation方法1");
    }

    @Override
    public void operation02() {
        System.out.println("B1实现了Interface1中的operation方法2");
    }

    @Override
    public void operation03() {
        System.out.println("B1实现了Interface1中的operation方法3");
    }
}
class D1 implements Segregation02,Interface2{
    @Override
    public void operation01() {
        System.out.println("D1实现了Segregation01中的operation方法1");
    }

    @Override
    public void operation04() {
        System.out.println("D1实现了Interface2中的operation方法4");
    }

    @Override
    public void operation05() {
        System.out.println("D1实现了Interface2中的operation方法5");
    }
}
class A1 {
    public void depend01(Segregation02 seg){
        seg.operation01();
    }
    public void depend02(Interface1 interface1){
        interface1.operation02();
    }
    public void depend03(Interface1 interface1){
        interface1.operation03();
    }
}
class C1 {
    public void depend01(Segregation02 seg){
        seg.operation01();
    }
    public void depend04(Interface2 interface2){
        interface2.operation04();
    }
    public void depend05(Interface2 interface2){
        interface2.operation05();
    }
}
3、依赖倒转(倒置)原则
概念
  1. 高层模块不应该依赖底层模块,二者都应该依赖其抽象
  2. 抽象不应该依赖于细节,细节应该依赖其抽象
  3. 依赖倒置原则的中心思想是面向接口编程
  4. 相对于细节的多边性,抽象的事物要稳定的多,在Java中,抽象相当于是接口,细节相当于是接口的实现类
  5. 展现事物的细节交给实现类去完成
案列分析

没有使用依赖倒置原则

package com.tedu.principle.inversion;
//依赖倒置原则
public class DependenceInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }
}
class Email{
    public String getInfo(){
        return "电子邮件信息,Hello World!";
    }
}
//完成person接收消息的功能
//方式1:不使用依赖倒置原则
/**
 * 如果我们想增加获取对象:微信,短信,qq等,那么会增加类,增加person的方法(显然不可取)
 */
class Person{
    public void receive(Email email){
        System.out.println(email.getInfo());
    }
}

使用了依赖倒置原则

package com.tedu.principle.inversion;
//依赖倒置原则
public class DependenceInversion02 {
    public static void main(String[] args) {
        Person1 person = new Person1();
        person.receive(new Email1());
        person.receive(new WeiXin());
    }
}
//定义接口
interface IReceive{
    public String getInfo();
}
class Email1 implements IReceive{
    public String getInfo(){
        return "电子邮件信息,Hello World!";
    }
}

class WeiXin implements IReceive{

    @Override
    public String getInfo() {
        return "微信接受的信息:Hello";
    }
}
//完成person接收消息的功能
//方式1:不使用依赖倒置原则

/**
 * 如果我们想增加获取对象:微信,短信,qq等,那么会增加类,增加person的方法(显然不可取)
 * 解决思路:引入一个抽象的接口:IReceive,表示接收者,这样Person类和IReceive发生依赖
 *          因为Email,微信等都属于接收者的范围,他们各自实现了IReceive就ok,这样就符合依赖倒置原则
 */
class Person1{
    public void receive(IReceive iReceive){
        System.out.println(iReceive.getInfo());
    }
}
依赖倒置原则常用的三种方式及其代码示例

接口传递:
代码示例

interface OpenAndClose01{
    public void open(Television1 television);
}
interface Television1{
    public void play();
}
class OpenAndClose1 implements OpenAndClose01{

    @Override
    public void open(Television1 television) {
        television.play();
    }
}

构造方法传递:
代码示例

interface OpenAndClose02{
    public void open();
}
interface Television2{
    public void play();
}
class OpenAndClose2 implements OpenAndClose02{
    public Television2 television2;
    public OpenAndClose2(Television2 television2){
       this.television2 = television2;
    }

    @Override
    public void open() {
        this.television2.play();
    }
}

setter传递
代码示例

interface OpenAndClose03{
    public void open();
    public void setTelevision(Television3 television);
}
interface Television3{
    public void play();
}
class OpenAndClose3 implements OpenAndClose03{
    private Television3 television3;

    @Override
    public void open() {
        this.television3.play();
    }

    @Override
    public void setTelevision(Television3 television3) {
        this.television3 = television3;
    }
}
注意事项和细节
  1. 低层模块尽量要有抽象类或者是接口,或者两者都有,程序的稳定性更好;
  2. 变量的声明类型尽量是接口,这样方便以后的程序扩展和优化
  3. 继承时遵循里氏替换原则
4、里氏替换原则
继承存在的问题
  1. 父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然他不强制要求所有的子类必须遵守这些契约,但如果子类对这些已经实现的方法任意更改,就会对整个系统造成破坏,
  2. 继承再给程序设计带来便利的同时,也带了弊端,比如使用继承会给程序带来侵入性,程序的课移植性降低,增加对象的耦合性。则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障;
  3. 解决上述问题:里氏替换原则
概念
  1. 在继承过程中,在子类中尽量不要重写父类的方法
  2. 在适当的情况下可以通过聚合和组合和依赖解决这些问题
案列分析

在不小心情况下重写了代码

package com.tedu.principle.liskov;

public class Liskov {
    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));
        System.out.println("1-8=" + b.func1(1, 8));
        System.out.println("11+3+9=" + b.func2(11, 3));
    }
}
//A类,返回两个数的差
class A {
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}
//增加功能,但是出现问题
/**
 * 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,
 * 造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,
 * 这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候
 *
 * 解决办法:
 * 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,
 * 聚合,组合等关系代替.
 */
class B extends A {
    @Override
    public int func1(int num1, int num2) {
        return num1 + num2;
    }
    public int func2(int num1, int num2) {
        return func1(num1, num2) + 9;
    }
}

使用里氏替换原则的代码:

package com.tedu.principle.liskov;

import java.util.Base64;

public class ImproveLiskov {
    public static void main(String[] args) {
        A1 a = new A1();
        System.out.println("11-3=" + a.func1(11, 3));
        System.out.println("1-8=" + a.func1(1, 8));

        System.out.println("----------- ");
        B1 b = new B1();
        System.out.println("11-3=" + b.func1(11, 3));
        System.out.println("1-8=" + b.func1(1, 8));
        System.out.println("11+3+9=" + b.func2(11, 3));
    }
}
class Base{
    //将更加基础的方法和属性写进基类中
}
//A类,返回两个数的差
class A1 extends Base {
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}

//增加功能,

class B1 extends Base {
    //如果B1要使用A1中的方法,使用组合关系
    private A1 a = new A1();
    public int func1(int num1, int num2) {
        return num1 - num2;
    }

    public int func2(int num1, int num2) {
        return func1(num1, num2) + 9;
    }
    public int func3(int num1,int num2){
        return a.func1(num1,num2);
    }
}

注意事项和细节

我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候

5、开闭原则
概念

基本介绍

  1. 编程中最基础最重要的原则
  2. 用抽象构建框架,用实体扩展细节
  3. 通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码实现变化
  4. 使用设计模式的母的就是遵循开闭原则
案列分析
package com.tedu.principle.ocp;
//开闭原则
public class Ocp {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
    }
}
//这是一个用于绘图的类
class GraphicEditor{
    //接受shape的对象,然后根据不同的type来绘制不同的图形
    public void drawShape(Shape shape){
        if (shape.m_type==1){
            drawRectangle(shape);
        }else if(shape.m_type==2){
            drawCircle(shape);
        }
    }
    private void drawRectangle(Shape shape) {
        System.out.println("绘制矩形");
    }
    private void drawCircle(Shape shape) {
        System.out.println("绘制圆形");
    }
}
//基类
class Shape{
    int m_type;
}
//矩形类
class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1;
    }
}
//圆形类
class Circle extends Shape{
    Circle(){
        super.m_type = 2;
    }
}

上述代码存在问题:如果想要新增一个新的图形,就会违背ocp原则中的第三点
新增之后的代码

package com.tedu.principle.ocp;
//开闭原则
public class Ocp {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
    }
}
//这是一个用于绘图的类
class GraphicEditor{
    //接受shape的对象,然后根据不同的type来绘制不同的图形
    public void drawShape(Shape shape){
        if (shape.m_type==1){
            drawRectangle(shape);
        }else if(shape.m_type==2){
            drawCircle(shape);
        }else if(shape.m_type == 3){
            drawTriangle(shape);
        }
    }
    private void drawRectangle(Shape shape) {
        System.out.println("绘制矩形");
    }
    private void drawCircle(Shape shape) {
        System.out.println("绘制圆形");
    }
    //新增绘制三角形
    private void drawTriangle(Shape shape){
        System.out.println("绘制三角形");
    }
}
//基类
class Shape{
    int m_type;
}
//矩形类
class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1;
    }
}
//圆形类
class Circle extends Shape{
    Circle(){
        super.m_type = 2;
    }
}
//新增三角形
class Triangle extends Shape{
    Triangle(){
        super.m_type = 3;
    }
}

使用开闭原则的代码分析

package com.tedu.principle.ocp;

public class ImproveOcp {
    public static void main(String[] args) {
        GraphicEditor1 graphicEditor = new GraphicEditor1();
        graphicEditor.drawShape(new Rectangle1());
        graphicEditor.drawShape(new Circle1());
        graphicEditor.drawShape(new Triangle1());
        graphicEditor.drawShape(new OtherGraph());
    }
}
/**
 * 鉴于在Ocp中出现的问题的解决思路:
 *
 */
//这是一个用于绘图的类
class GraphicEditor1{
    //接受shape的对象,然后根据不同的type来绘制不同的图形
    public void drawShape(Shape1 shape) {
        shape.draw();
    }
}
//基类
abstract class Shape1{
    int m_type;

    public abstract void draw();
}
//矩形类
class Rectangle1 extends Shape1{
    Rectangle1(){
        super.m_type = 1;
    }

    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}
//圆形类
class Circle1 extends Shape1{
    Circle1(){
        super.m_type = 2;
    }
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}
//新增三角形
class Triangle1 extends Shape1{
    Triangle1(){
        super.m_type = 3;
    }
    @Override
    public void draw() {
        System.out.println("绘制三角形");
    }
}
//再次新增
class OtherGraph extends Shape1{
    OtherGraph(){
        super.m_type = 4;
    }
    @Override
    public void draw() {
        System.out.println("绘制其他图形");
    }
}
6、迪米特法则
概念

基本介绍

  1. 一个对象对其他对象保持很少的了解
  2. 迪米特法则又叫最少知道原则:即一个类在对自己依赖的类知道的越少越好。即对于被依赖的类,该类的信息都尽量的放在类的内部,而只提供类的public方法
  3. 确定朋友的条件(是否满足迪米特法则)
    1)当前对象本身(this)
    2)以参量形式传入到当前对象方法中的对象
    3)当前对象的实例变量直接引用的对象
    4)当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友
    5)当前对象所创建的对象
    
案列分析

不遵守迪米特法则:

package com.tedu.principle.demeter;

import java.util.ArrayList;
import java.util.List;

//不遵守迪米特法则
public class Demeter {
    public static void main(String[] args) {
        SchoolManager manager = new SchoolManager();
        manager.printAll(new CollegeManager());
    }
}
//学校总部职工
class Employee{
    private String id;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}
//学院里的员工类
class CollegeEmployee{
    private String id;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}
// 管理学院员工的类
// CollegeEmployee 不满足迪米特法则条件
class CollegeManager{
    public List<CollegeEmployee> getAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee employee = new CollegeEmployee();
            employee.setId("学院员工 = " + i);
            list.add(employee);
        }
        return list;
    }
}
//学校管理
class SchoolManager{
    public List<Employee> getAllEmployee(){
        List<Employee> list = new ArrayList<Employee>();
        for (int i = 0; i < 5; i++) {
            Employee employee = new Employee();
            employee.setId("学院总部员工 = " + i);
            list.add(employee);
        }
        return list;
    }
    //该方法完成输出学校总部和学院员工信息的方法
    void printAll(CollegeManager manager){
        List<CollegeEmployee> list1 = manager.getAllEmployee();
        System.out.println("-----学院员工-----");
        for (CollegeEmployee employee: list1 ) {
            System.out.println(employee.getId());
        }
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("-----学院总部员工-----");
        for (Employee employee: list2 ) {
            System.out.println(employee.getId());
        }
    }
}

遵守迪米特法则:

package com.tedu.principle.demeter;

import java.util.ArrayList;
import java.util.List;

//不遵守迪米特法则
public class Demeter2 {
    public static void main(String[] args) {
        SchoolManager manager = new SchoolManager();
        manager.printAll(new CollegeManager());
    }
}
//学校总部职工
class Employee1{
    private String id;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}
//学院里的员工类
class CollegeEmployee1{
    private String id;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}
// 管理学院员工的类
class CollegeManager1{
    public List<CollegeEmployee1> getAllEmployee(){
        List<CollegeEmployee1> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee1 employee = new CollegeEmployee1();
            employee.setId("学院员工 = " + i);
            list.add(employee);
        }
        return list;
    }
    public void printAll(){
        List<CollegeEmployee1> list1 = this.getAllEmployee();
        System.out.println("-----学院员工-----");
        for (CollegeEmployee1 employee1: list1 ) {
            System.out.println(employee1.getId());
        }
    }
}

class SchoolManager1{
    public List<Employee1> getAllEmployee(){
        List<Employee1> list = new ArrayList<Employee1>();
        for (int i = 0; i < 5; i++) {
            Employee1 employee = new Employee1();
            employee.setId("学院总部员工 = " + i);
            list.add(employee);
        }
        return list;
    }
    //该方法完成输出学校总部和学院员工信息的方法
    void printAll(CollegeManager1 manager){
        /*List<CollegeEmployee> list1 = manager.getAllEmployee();
        System.out.println("-----学院员工-----");
        for (CollegeEmployee employee: list1 ) {
            System.out.println(employee.getId());
        }*/
        manager.printAll();
        List<Employee1> list2 = this.getAllEmployee();
        System.out.println("-----学院总部员工-----");
        for (Employee1 employee: list2 ) {
            System.out.println(employee.getId());
        }
    }
}
注意事项和细节

降低类之间的耦合关系,并不是完全没有依赖

7、合成复用原则
概念

尽量使用合成聚合的方式来书写代码,而不是使用继承。

总结:
  1. 将应用中可能需要变化之处的代码独立出来,别和不需要变化的代码混在一起
  2. 针对接口编程,而不是针对实现编程
  3. 为了交互对象之间的松耦合设计
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值