欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java设计模式基础——设计原则(一)

程序员文章站 2022-06-13 14:00:48
...

引入

图解Java模式笔记

什么是设计模式

对于有些普遍存在(反复出现)的问题,所提出的解决方案

Java代码中哪里有体现到?

拿一道经典的面试题来说

1)有请使用UML类图画出原型模式核心角色
2)原型设计模式的深拷贝和浅拷贝是什么,并写出深拷贝的两种方式的源码(重写
clone方法实现深拷贝、使用序列化来实现深拷贝)
3)在Spring框架中哪 里使用到原型模式,并对源码进行分析
beans.xml
<bean id="id01" class="com.atguigu.spring bean.Monster" scope="prototype"/>

scope=“prototype”

就是指出我们的原型模式是什么,是哪一种
Java设计模式基础——设计原则(一)

还有

**金融借贷平台项目:**借贷平台的订单,有审核-
发布-抢单等等步骤,随着操作的不同,会改
变订单的状态,项目中的这个模块实现就会使用
到状态模式,请你使用状态模式进行设计,并
完成实际代码
问题分析:
这类代码难以应对变化,在添加一种状态时,我们需要手动添加if/else,在添加一种功能时,要对所有的状态进行判断。因此代码会变得越来越臃肿,并且-旦没有处理某个状态,便会发生极其严重的BUG,难以维护,但是状态模式这种Java设计模式可以帮我们即安全又快速的实现后面我们将会详细学习状态模式

设计模式的好处(目的)

  • 提高代码重用性
  • 提高代码可读性
  • 提高代码可扩展性
  • 提高代码可靠性(安全性)
  • 是程序实现高内聚,低耦合

一.七大设计原则

有哪些设计原则

  • 单一职责原则
  • 接口隔离原则
  • 抵赖倒转(倒置)原则
  • 里氏替换原则、
  • 开闭原则
  • 迪米特法则
  • 合成复用原则

单一职责原则

定义

对类来说,即一个类应该只负责一项职责,如果A类负责两个不同的职责:职责1,职责2,当职责1的需求改变时,可能会造成职责执行错误,所以要将类A的粒度分解为A1,A2

就是说我们这个类只能做我们需要做的,分好类,别你的管理数据的类,又去管理控制层

应用实例

public class Singleresponsibility1 {
    public static void main(String[] args) {
        Bus bus = new Bus();
        bus.run("自行车");
        bus.run("摩托车");
        bus.run("飞机");
    }
}
//我想这个管理陆地上的交通工具
class Bus{
    public void run(String bus){
        System.out.println(bus+"在公路上跑");
    }
}
结果
    自行车在公路上跑
    摩托车在公路上跑
    飞机在公路上跑

我这个类明显想的是控制陆地上的交通工具,而天上的不想管,但是这个类中就有了天上的,明显不符合单一职责原则

我们就可以根据方式不同来细分我们的类,将类的粒度分解陆地,天空,海路等等

改进

public class Singleresponsibility1 {
    public static void main(String[] args) {
        Road road = new Road();
        road.run("自行车");
        road.run("高铁");
        Water water = new Water();
        water.run("轮船");
        Air air = new Air();
        air.run("客机");

    }
}
//我想这个管理陆地上的交通工具
class Road{
    public void run(String vehicle){
        System.out.println(vehicle+"在公路上跑");
    }
}
class Water{
    public void run(String vehicle){
        System.out.println(vehicle+"在水里游");
    }
}
class Air{
    public void run(String vehicle){
        System.out.println(vehicle+"在天空飞");
    }
}
自行车在公路上跑
高铁在公路上跑
轮船在水里游
客机在天空飞

分析:

遵守了单一职责原则

但是改动很大,既要将我们的类分解成好几个类,也要去修改主函数

再次改进

public class Singleresponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("自行车");
        vehicle.run("高铁");
        vehicle.runAir("轮船");
        vehicle.runWater("客机");
    }
}
//我想这个管理陆地上的交通工具
class 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+"在海中游");
    }
}
自行车在公路上跑
高铁在公路上跑
轮船在天空中飞
客机在海中游

这种方法没有对原来的类做太大修改,只是增加方法

虽然在类的级别上没有遵守单一职责原则,但是在方法的级别上遵守了单一职责原则

小结

降低代码复杂度,增加代码的可读性,提高代码可维护性

降低变更的风险(如果是第一次改进,在类上遵守单一职责原则,我们修改一个类,绝对不会影响到另一个类的逻辑)

我们的第三种其实是不严谨的,只是我们的例子简单,复杂的话,还要从类的级别上遵守单一职责原则

接口隔离原则

定义

客户端不应该依赖它不需要的接口,即一个类中对另外一个类的依赖应该建立在最小的接口中

如图

Java设计模式基础——设计原则(一)

也就是说类A类C必须实现接口中的全部方法,这就不是一个类中对另外一个类的依赖应该建立在最小的接口

按照我们的接口隔离原则应当这样处理

讲接口拆分成独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则

代码

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

    }
}
//接口
interface Interface1{
    public void method1();
    public void method2();
    public void method3();
    public void method4();
    public void method5();
}
class B implements Interface1{
    @Override
    public void method1() {
        System.out.println("B类方法1");
    }
    @Override
    public void method2() {
        System.out.println("B类方法2");
    }
    @Override
    public void method3() {
        System.out.println("B类方法3");
    }
    @Override
    public void method4() {
        System.out.println("B类方法4");
    }
    @Override
    public void method5() {
        System.out.println("B类方法5");
    }
}
class D implements Interface1{
    @Override
    public void method1() {
        System.out.println("D类方法1");
    }
    @Override
    public void method2() {
        System.out.println("D类方法2");
    }
    @Override
    public void method3() {
        System.out.println("D类方法3");
    }
    @Override
    public void method4() {
        System.out.println("D类方法4");
    }
    @Override
    public void method5() {
        System.out.println("D类方法5");
    }
}

class A {
    public void depend1(Interface1 i){
        i.method1();
    }
    public void depend2(Interface1 i){
        i.method2();
    }
    public void depend3(Interface1 i){
        i.method3();
    }
}
class C{
    public void depend1(Interface1 i){
        i.method1();
    }
    public void depend3(Interface1 i){
        i.method4();
    }
    public void depend4(Interface1 i){
        i.method5();
    }
}

我们发现,A依赖B但是不需要45方法,C依赖D但不需要23方法

所以我们对接口分层,让他满足接口隔离原则

改进

public class Segregation1 {
    public static void main(String[] args) {
        A a = new A();
        a.depend1(new B());//A类通过接口去依赖B
        a.depend2(new B());
        a.depend3(new B());
        C c  =new C();
        c.depend1(new D());
        c.depend4(new D());
        c.depend5(new D());
    }
}
//接口
interface Interface1{
    public void method1();
}
interface Interface2{
    public void method2();
    public void method3();
}
interface Interface3{
    public void method4();
    public void method5();
}
class B implements Interface1,Interface2 {
    @Override
    public void method1() {
        System.out.println("B类方法1");
    }
    @Override
    public void method2() {
        System.out.println("B类方法2");
    }
    @Override
    public void method3() {
        System.out.println("B类方法3");
    }

}
class D implements Interface1,Interface3{
    @Override
    public void method1() {
        System.out.println("D类方法1");
    }
    @Override
    public void method4() {
        System.out.println("D类方法4");
    }
    @Override
    public void method5() {
        System.out.println("D类方法5");
    }
}

class A {
    public void depend1(Interface1 i){
        i.method1();
    }
    public void depend2(Interface2 i){
        i.method2();
    }
    public void depend3(Interface2 i){
        i.method3();
    }
}
class C{
    public void depend1(Interface1 i){
        i.method1();
    }
    public void depend4(Interface3 i){
        i.method4();
    }
    public void depend5(Interface3 i){
        i.method5();
    }
}

我们将一个接口分为了3个接口,分成最小的接口,然后再去让类实现依赖

这就是接口隔离原则的概念

通俗的来讲就是一个类如果需要用到那个接口的方法,但不是那个接口的全部方法,我们就可以拆分接口

依赖倒转原则

什么是依赖倒转

  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转的中心思想是面向接口编程
  • 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类
  • 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

依赖倒转的实现

public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }
}
class Email{
    public String getInfo(){
        return "电子邮件信息:hello,world";
    }
}
//接收消息功能
//方式一
class Person{
    public void receive(Email email){
        System.out.println(email.getInfo());
    }
}

弊端是:

我们发现我们我们的receive要传递一个类的对象,也就是说,我们每次调用该方法,都要新创建一个类,同时那个类也要有相应的接收方法

解决思路:

引入一个抽象的接口XXXXX,表示接收者,这样我们的Person与接口依赖

因为Emali,weixin等等属于接受的范围,他们实现XXX接口就OK了,这样我们就符合了依赖倒转原则

改进

public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
        person.receive(new WeiXin());
    }
}
//定义接口
interface IReceiver{
    public String getInfo();
}
class Email implements IReceiver{
    @Override
    public String getInfo(){
        return "电子邮件信息:hello,world";
    }
}
class WeiXin implements IReceiver{

    @Override
    public String getInfo() {
        return "微信的消息:hello,OK";
    }
}

//接收消息功能
//方式一
class Person{
    public void receive(IReceiver receiver){
        System.out.println(receiver.getInfo());
    }
}

我们发现就算我们新增了一个其他方式的接收类,只要我们客户端传递的对象是对应的类型就可以了,我们的Person是不需要进行任何改动的

依赖关系传递的三种方式

接口传递

Java设计模式基础——设计原则(一)

也就是说,我们的依赖关系是接口类型在传递

ITV tv = new ITV();
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.open(tv);

构造方法传递

	interface IOpenAndClose {
		public void open(); //抽象方法
	}
	interface ITV { //ITV接口
		public void play();
    }	
	class OpenAndClose implements IOpenAndClose{
	public ITV tv; //成员
	public OpenAndClose(ITV tv){ //构造器
		this.tv = tv;
	}
	public void open(){
		this .tv. p1ay();
	}
    ITV tv = new ITV();
    OpenAndClose openAndClose = new OpenAndClose(tv);
	openAndClose.open();

setter方式传递

interface IOpenAndClose {
	public void open(); //抽象方法
	public void setTv(ITV tv);
}
interface ITV { // ITV接口
	public void p1ay();
}
class OpenAndClose implements IOpenAndClose {
	private ITV tv;
	public void setTv(ITV tv) {
		this.tv = tv;
	}
	public void open() {
		this . tv. play();
	}
}
	ITV tv = new ITV();
    OpenAndClose openAndClose = new OpenAndClose();
	openAndClose.setTv(tv);
	openAndClose.open();

这一点很像我们bean工厂怎么实例化