java中设计思想及设计原则及设计模式解析

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中设计思想及设计原则及设计模式解析相关的知识,希望对你有一定的参考价值。

前言

在java开发过程中,或者各种语言在编程过程中,对基础知识的累计,这其中就会包含java的设计思想及设计原则及设计模式;我们想对当前的架构以及某些功能逻辑进行优化,及繁简,更加适合当前业务的开发,如何让人一看就能懂得代码,以及各种热门框架 包括spring mybatis 等等热门框架,我们想对其中进行研究,以及或者自己写一个比较好得框架,这一定是离不开得。除了java基础知识,优化, 这就是 研究设计原则和设计模式得意义对于开发,深层次得了解,有着更大的意义,废话不多说 从设计思想开始说起。 

OOP思想

面向对象程序设计(Object Oriented Programming,OOP)是一种计算机编程架构。OOP的一条基本原则是计算机程序由单个能够起到子程序作用的单元或对象组合而成。OOP达到了软件工程的三个主要目标:重用性、灵活性和扩展性。OOP=对象+类+继承+多态+消息,其中核心概念是类和对象。

在其中很重要的概念 ,类 接口  抽象类  

 类是做什么用的

模拟现实,封装数据与代码,所有的业务逻辑等 ,都应该在类中实现。
接口是做什么用
接口就是类与类之间调用, 类之间的交互规范。

 定义功能使用者和功能提供者间的接口

为什么要有接口,隔离开变化  这是他存在的意义。

抽象类是做什么用
包容不变与变的
带来的好处 模拟现实生活,代码易于理解与维护。

三大特性

oop衍生出三大特性  封装 、继承、多态

  • 封装带给我们的好处   例如  可以模块化,就是分工比较明确 重用、不必关心具体的实现 而且具有安全性。
  • 继承 ,也是为我们在开发过程 减少大量代码的写作,易于管理,继承避免了对一般类和特殊类之间共同特征进行的重复描述。
  • 多态  一种实现变化的方式

OOP中复用的形式

两种方式,继承  和组合 的方式。

组合,类2持有类1的实例 类2使用类1

如果你是有一定的开发经验,就会非常容易理解,包括源代码也好  还是  自己写的逻辑也罢,都是这样进行往上进行扩展的, 逃不开这个

继承和 组合的方式。 在实际开发中非常有用。

为什么要使用设计模式

当oop的这些特性,怎么在开发中使用起来 玩转,充分使用。 就简单使用 oop三大特性是不能满足的。然后衍生出了 设计模式。

在实际开发中,也是 尽量的减少代码的修改 这就是我们最终想达到的效果。 

指导我们 如何撸代码  如何写接口  和类 写方法。

代码会变,为应对变化,为了以后方便扩展。  做到以不变应万变,   不变的是变化! 这也是为什么spring框架为什么这么火的原因。

使用设计模式

 理清现实  区分变与不变 搞清楚会如何变 使用者如何隔绝变化  这些都是要我们在现实中需要思考 这都是在开发中遇到的,有些地方根据需求就要变化 功能增强等等。 有些功能是不可能变得。 但是要找到变化得部分 抽象出来,这都是 在平常开发中肯定会遇到得。

设计的体现:如何来定义类、接口、方法

隔离,封装变化的部分,让其他部分不受它的影响。

不同得变化方式,就需要使用不同得设计模式,

设计原则

设计原则 是面向对象的指导思想,其实有时候在开发过程中你并没有特意去写设计模式,但是重构代码时,一不小心就写除了 设计模式。

  • 1. 单一职责原则 SRP
一个类只有一个引起修改变化的原因,也就是只负责一个职责。核心思想: 高内聚,低耦合
  • 2. 开闭原则 OCP
对模块类的操作, 对扩展开放,对修改关闭 。经过多年验证的代码,被你一改变Bug。
  • 3. 里氏替换原则 LSP
超类 可以 被替换 为其 子类 来使用。这样就能做到代码不用修改,能够任意切换不同实现的子类, 少修改复用
  • 4. 依赖倒置原则 DIP
程序依赖于抽象接口,不依赖于具体实现, 降低 实现模块间的 耦合度 。你不要来找(new)我,我会来找(set)你。
  • 5. 接口隔离原则 ISP
不该强迫客户程序依赖不需要使用的方法,一个接口只提供对外的功能,不是把所有功能封装进去。 减少依赖范围
  • 6. 组合复用原则 CRP
尽量使用组合,而不是继承来达到复用的目的, 继承强耦合,组合低耦合 ,组合还能运行时 动态替换
  • 7. 迪米特法则 LoD
一个对象应当对其他对象有尽可能少的了解,不和陌生人说话, 降低 各个对象之间的 耦合 ,提高系统的可维护性。

这些其实告诉我们 ,代码尽量复用 ,别对以前的代码进行  进行修改呀 单一职责,这都是 上面原则要告诉我们的事。

这其中也涉及相对矛盾的地方,比如里氏替换 和组合复用。都需要根据不同的场合使用的。

设计模式

应用设计模式的目的:易扩展,易维护 少改代码,不改代码

策略模式

应用场景

 对于不同的活动的实现。后端代码中如何来灵活应对订单金额 

OrderController

使用switch case的代码。

 这虽然没有使用 策略模式,但是我在看到不同的业务场景,在项目上也发现了很多,也是很简单的。场景比较少的情况。就采用这种方式,也没问题的。也是在于不同的应用场景不同的使用方式。

策略模式的实现

单一职责原则
营销活动经常变,这个switch就得经常改, 还得不断加促销的算法方法……
这里变的是什么?
促销的金额的算法!同一行为的不同算法! 我们不希望OrderService被算法代码爆炸!
这也是产生策略模式的实现,  单一职责原则。
同一行为的不同算法实现,我们可以用接口来定义行为,不同的算法分别去实现接口。
设计原则:对修改关闭,对扩展开放!

利用类来将变化部分 抽象出来。

定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互
替换,让算法独立于使用它的用户而独立变化。

 其实策略模式 就是利用类来将变化部分 抽象出来。  但是把真正使用时,肯定需要结合抽象工厂模式来使用,因为只是简单使用接口去隔离开确实达不到想要的效果,对于类也相对来说会继续增加的。

工厂模式

 工厂模式也就是为了将我们我们创建类给抽象出来 不要让我们自己来一个业务需求就创建一个类。

 

 简单工厂模式:所有产品由一个工厂创建

简单工厂模式 工厂中的代码也不要随促销的变化而变化  

而在spring中 怎么利用工厂去创建类

  • promotion = spring beanName
  • 配置promotion与实现类的对应关系

 通过一个map的方式 存储起来,根据不同的beanname去创建对应的类。

public class PromotionCalculationFactory 

	private Map<String, PromotionCalculation> maps;
	/*
	public PromotionCalculation getPromotionCalculation(String promotion) 
		switch() 
		// ......
		
	
	*/
	public PromotionCalculation getPromotionCalculation(String promotion) 
		PromotionCalculation prom = maps.get(promotion);
		if (prom == null) 
			// 从配置的地方加载
			prom = getFromDb(promotion);
			if (prom != null)
				maps.put(promotion, prom);
		

		return prom;
	
	// spring bean初始化方法
	public void init() 
		// 第一次将所有的促销策略都加载到Map中
	
	
	// 从数据库加载配置信息,也可以从配置文件加载
	private PromotionCalculation getFromDb(String promotion) 
		// 从数据库中取到对应的类名
		//配置的格式: promotion1=com.study.dn.promotion.calculation.Promotion1
		// TODO String className = 从数据库(或其他配置源)中获得;
		// 加载类信息
		// TODO Class c = Class.forName(className);

		// 实例化
		
		// 返回
		return null;
	

这就是一个简单工厂得实现形式。

一个工厂负责创建所有实例

工厂方法模式

父类中定义工厂方法,各子类实现 具体的实例创建
在运行时才决定创建使用那个子类去创建类,更加抽象起来。
具体得需要具体实现。更加抽象出来。父类定义抽象出来。

抽象工厂模式

定义一个工厂接口,所有具体工厂实现接口

 通过factorypaoducer去选择创建工厂类代工厂。利用接口去抽象出工厂,然后利用工厂去创建创建不同的类。

不能过度设计的,只有变化需求,扩展时,才使用的。

装饰者模式

灵活的将功能进行叠加,这里就需要说到装饰着模式了。

相当于俄罗斯套娃那样。将功能一层层进行叠加。

以装饰的方式,动态地将责任附加到对象上
不改变具体类代码,动态叠加增 强行为功能。

若要扩展功能,装饰者提供了比 继承更有弹性的替代方案
两个都实现相同的接口,弹性的扩展设计。允许行为可以被扩展,基于原来的基础上进行扩展。接口的聚合 组合就可以对于扩展。
在jdk中,io确实用到的

 fileterinputstream  委托给 tuputstream 进行操作 并附加增强功能  在使用时则需要将需要装饰的对象传进去 这就是。组合 替代了 继承的操作。

 包含一个被装饰者,基于被装饰者实现的前后增强实现逻辑,对方法结果进行修改,扩展功能。

/**
 * 功能增强
 */
public class DecoratorA extends Decorator 

	public DecoratorA(Component component) 
		super(component);
	

	public String methodA() 
		return this.component.methodA() + " + A";
	

	public int methodB() 
		return this.component.methodB() + 10;
	

达到对于功能进行增强了

在原来的基础上不断进行增强。 

责任链和装饰者模式完成的是相同 的事情。

代理模式

为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
作用:不改变原类的代码,而增强原类对象的功能 可选择前置、后置、环绕、异常处理增强
类图与装饰者模式一样
与装饰者模式的区别
意图的不同:代理模式意在在代理中 控制 使用者对目标对象的访问, 以及进行功能增强。

 

 其他的 任何调用,都让代理者进行操作。

代理模式:控制访问者的权限,封装起来,只暴露部分方法。

代理模式-实现方式

静态代理:

程序员创建或由特定工具自动生成代理类源代码,再对其编
译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:
代理类在程序运行时,运用反射机制动态创建而成。 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
什么cglib jdk动态代理。这些 都是一个实现。
首先提供个接口  暴露出来的。

public interface Girl 
	boolean dating(float length);

 然后  代理类 判断是否可以使用

/**
 * 经纪人公司,JDK动态代理实现
 *
 */
public class TonyCompany 
	
	public static Object proxy(Object target) 
		// 只能基于接口代理
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(),
				new MyInvationHandler(target));
	
	
	// 功能增强实现
	private static class MyInvationHandler implements InvocationHandler 

		private Object target;

		public MyInvationHandler(Object target) 
			super();
			this.target = target;
		

		public Object getTarget() 
			return target;
		

		public void setTarget(Object target) 
			this.target = target;
		

		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
			// 前置增强
			doSomethingBefore();

			// 调用被代理对象的方法
			Object res = method.invoke(target, args);

			// 后置增强
			doSomethingAfter();

			return res;
		

		private void doSomethingAfter() 
			System.out.println("");
		

		private void doSomethingBefore() 
			System.out.println("");
		

	

 持有代理的对象。

/**
 * 静态代理的实现
 */
public class Tony implements Girl 

	private Girl girl;

	public Girl getGirl() 
		return girl;
	

	public void setGirl(Girl girl) 
		this.girl = girl;
	

	public boolean dating(float length) 
		// 前置增强
		doSomethingBefore();
		boolean res = this.girl.dating(length);
		// 后置增强
		doSomethingAfter();
		return res;
	

	private void doSomethingBefore() 
		System.out.println("老板,这个我试过了,很不错,推荐给你!");
	
	
	private void doSomethingAfter() 
		System.out.println("老板,你觉得怎样,欢迎下次再约!");
	


 最后达到代理的效果

 静态代理的差点就是  要根据不同代理对象去修改。

扩展能力差

横向扩展:代理更多的类
纵向扩展:增强更多的方法

动态代理:动态不同为类的对象创建代理。增强代理。灵活实现:

实现方式

  • JDK动态代理:只可对接口创建代理
  • CGLIB动态代理:可对接口、类创建代理

整个动态代理,需要代理的接口 ,new出来被代理的对象。也就是 动态给我们创建一个$proxy,也就是不用我们自己去创建这个类。因此 需要 invocationhandler 进行动态代理。

把需要增强的功能代码在 invocationhandler上实现就行。

基于接口的动态代理。代理对象给我就行。

	Girl tony1 = (Girl) TonyCompany.proxy(tc);
		th.dating(tony1);

这里就会产生一个代理的实现类

这都是动态代理应该调用到的, 包括权限校验等等。

 

cglib动态代理 

A powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime. 一个高层次的java字节码生成和转换的api库.

 

生成字节码。

ASM:一个低层次的字节码操作库

主要作用

在运行期为类、接口生成动态代理对象。
以达到不改动原类代码而实现功能增强的目的

 

使用和实现方式和jdk中动态代理是很像的。

 

 代理的实现类。

 

 

 会到具体的方法上面去。

责任链模式

最常见的应用就是springmvc中的使用。 

http web请求处理,请求过来后将经过转码、解析、参数封装、鉴权….一系列的处理(责任),而且要经过多少处理是可以灵活调整的。
责任链:所有的处理者,都加入到这个链式,一个处理完后,转给下一个。、
1、抽象出责任接口,具体责任逻辑实现接口。
2、根据处理过程需要,将具体实现组合成链。
3、使用者使用链。
典型代表: Filter、Interceptor

 

每一次请求的链条是可能不同的。抽象出变化的部分具体的接口。
当然各种实现 可以采用 链表,也可以采用数组来实现。 在netty中就使用的链表。
和装饰的区别  还是在不用把被装饰者包含起来。 并且重点在于 流水线

 

 

 如果当我们不需要下个链条是 不用执行下面代码就可以了。

	chain.process(request);

适配器模式

应用在 使用者依赖的接口与提供者的接口不匹配时,就加一层适配,而不改两端的代码。

使用转换器来将不符合的代码进行转换。

适配器的模式 将不同的接口连起来,但是针对的是类进行兼容。然后适配

 

 外观(门面)模式

应用场景:

使用方要完成一个功能,需要调用提供方的多个接口、方法,调用过程复杂时,我们可以再提供一个高层接口(新的外观),将复杂的调用过程向使用方隐藏。适配器模式的变种。
例如一键换机,快速进行操作。这些都是

 设计原则:迪米特原则,最少知识原则

只需要装饰起来  中介开。他会把所有要做的存储起来,这个在应用中使用的非常多,我们只需要一点就行,其他的,都帮我们做了。

观察者模式

这个模式在我们开发过程中也会遇到很多把,微信公众号,关注就可以收到推送的消息,取消关注,就不会再收到。  以及在什么消息中间件中的思想也是采用  也就是发布订阅。

变化之处:观察者会变,观察者的数量会变。
不变:主题的代码要不受观察者变化的影响。
定义了对象之间一对多的依赖关系,当一端对象改变状态时,它的所有依赖者都会收到通知并自动更新(被调用更新方法)。
也称为:监听模式、发布订阅模式。提供一种对象之间松耦合的设计方式。
设计原则:为了交互对象之间的松耦合设计而努力!
在jdk中提供给了我们观察者模式的通用实现。
Java.util. Observable  可被观察的(主题),具体主题扩展它。
java.util.Observer 观察者接口,具体观察者实现该接口。

 

 项目开发中 我之前用到的就是 android 应用启动时,将所有需要启动的类,都做一个启动方法。

命令模式

应用场景

系统设计一个命令行界面,用户可输入命令来执行某项功能。 系统的功能会不断添加,命令也会不断增加。

 对于命令不同的实现, 而且不固定 大量时,动态的添加

采用map的方式,去解决 这种 if else的方式,

并且将具体的功能给抽象出来,扩展成不同的格式来处理

这个和策略模式很像,但是不一样。

命令模式:
以命令的方式,解耦调用者与功能的具体实现者,降低系统耦合度,提供了灵活性。
适用场景:交互场景
实例:Servlet Controller 线程池

 而策略模式更加简单一点 ,不会来存储这个变化的部分。从而达到实现大量的变化。

策略模式侧重的是一个行为的多个算法实现,可互换算法。
命令模式侧重的是为多个行为提供灵活的执行方式。

状态模式

一个类对外提供了多个行为,同时该类对象有多种状态,不同状态下对外的行为的表现不同
状态的流转,需要一个状态去控制。
 

 根据状态去控制行为。

 

不同的状态下,这四种种操作将有不同的表现。
如在没有支付状态下,用户在咖啡机上点退款、购买、取咖啡,和在已支付的状态下做这三个操作。

 

 

 

 命令模式的变化部分 就是状态,所以需要把状态 抽象化。成为接口。对应的方法。 这也是整个设计模式需要,通过接口 或者抽象类去实现。

对比 状态模式-命令模式-策略模式

策略模式侧重的是一个行为的多个算法实现,可互换算法
命令模式侧重的是为多个行为提供灵活的执行方式。
状态模式,应用于状态机的情况。
设计原则:区分变与不变,隔离变化
设计原则:面向接口编程
设计原则:多用组合,少用继承

两个以上的模式进行组合,复合模式

桥接模式

一个画图程序,可以画各种颜色不同形状的图形。
比如有红、黄、蓝三种颜色
形状有方形、圆、三角形
圆可以是红圆、黄圆、蓝圆
会从两个维度发生变化:形状、颜色
解决的也是多维度上的变化

 

会导致类爆炸的, 发生变化的主要是颜色和图形 都会变化。

能抽象 但是组合起来是比较麻烦的。

而桥接模式就是解决这种  情况的。

任其在两个维度各自变化,为这两个维度搭个 桥,让它们可以融合在一起:桥接模式
抽象:分别对各个维度进行抽象,将共同部分抽取出来

组合:将抽象组合在一起(桥接)

桥接:将多个维度的变化以 抽象的方式组合在一起。使 用者面向抽象。各维度间解 耦,可自由变化。 这里就要利用抽象类进行处理了。

单例模式

减少类对象的创建

饥汉式 

 在实例化时就初始化好类对象。

public class Singleton 
    private final static Singleton INSTANCE = new Singleton();
    private Singleton()
    public static Singleton getInstance()
        return INSTANCE;
    
 

 

public class Singleton 
    private static Singleton instance;
    static 
        instance = new Singleton();
    
    private Singleton() 
    public static Singleton getInstance() 
        return instance;
    
 

 

懒汉式
在使用时才去创建类对象,因此可能需要考虑线程安全等。

 

public class Singleton 
    private static Singleton singleton;
    private Singleton() 
    public static synchronized Singleton getInstance() 
        if (singleton == null) 
            singleton = new Singleton();
        
        return singleton;
    
 

 缺点:实例化后就不应该再同步了,效率低。  

其实根据需要 还是实现采用双重检查

public class Singleton 
    private static volatile Singleton singleton;
    private Singleton() 
    public static Singleton getInstance() 
        if (singleton == null) 
            synchronized (Singleton.class) 
                if (singleton == null) 
                    singleton = new Singleton();
                 
            
        
        return singleton;
     
注意: volatile关键字修饰很关键
优点:线程安全;延迟加载;效率较高。
静态内部类方式

 

public class Singleton 
    private Singleton() 
    private static class SingletonInstance 
    private static final Singleton INSTANCE = 
        new Singleton();
    
    public static Singleton getInstance() 
        return SingletonInstance.INSTANCE;
     

优点:避免了线程不安全,延迟加载,效率高。

原理:类的静态属性只会在第一次加载类的时候初始化。在这里,JVM帮助我们保证了线程的安
全性,在类进行初始化时,别的线程是无法进入的。

 

只要调用getinstance就不会创建 singletoninstance类的。

用枚举的方式

public enum Singleton 
    INSTANCE;
    public void whateverMethod() 
 

 模板方法模式

当我们设计一个类时,我们能明确它对外提供的某个方法的内部执行步骤, 但一些步骤,不同的子类有不同的行为时

 

public abstract class Game 
    protected abstract void initialize();
    protected abstract void startPlay();
    protected abstract void endPlay();
    //模板方法
    public final void play()
        //初始化游戏
        initialize();
        //开始游戏
        startPlay();
        //结束游戏
        endPlay();
     

优点

1、封装不变部分,扩展可变部分。
2、提取公共代码,便于维护。
3、行为由父类控制,子类实现。
适用场景:
1、有多个子类共有的方法,且逻辑相同。
2、重要的、复杂的方法,可以考虑作为模板方法。

总结

设计模式总结

创建型模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
包括
  • 工厂模式(Factory Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)
  • 单例模式(Singleton Pattern)
  • 建造者模式(Builder Pattern)
  • 原型模式(Prototype Pattern)
结构型模式
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
包括
  • 适配器模式(Adapter Pattern)
  • 桥接模式(Bridge Pattern)
  • 组合模式(Composite Pattern)
  • 装饰器模式(Decorator Pattern)
  • 外观模式(Facade Pattern)
  • 享元模式(Flyweight Pattern)
  • 代理模式(Proxy Pattern)
行为型模式
这些设计模式特别关注对象之间的通信。
包括:
  • 责任链模式(Chain of Responsibility Pattern )
  • 命令模式(Command Pattern)
  • 解释器模式(Interpreter Pattern)
  • 迭代器模式(Iterator Pattern)
  • 中介者模式(Mediator Pattern)
  • 备忘录模式(Memento Pattern)
  • 观察者模式(Observer Pattern)
  • 状态模式(State Pattern)
  • 空对象模式(Null Object Pattern)
  • 策略模式(Strategy Pattern)
  • 模板模式(Template Pattern)
  • 访问者模式(Visitor Pattern)

设计原则总结

  • 变化隔离原则:找出变化,分开变化和不变的  隔离,封装变化的部分,让其他部分不受它的影响。
  • 面向接口编程 依赖倒置 隔离,封装变化的部分,让其他部分不受它的影响。 隔离变化的方式 使用者使用接口,提供者实现接口。“接口”可以是超类!
  • 多用组合,少用继承 灵活变化的方式
  • 最少知道原则 又称迪米特法则
  • 开闭原则:对修改闭合,对扩展开放 隔离变化的方式
  • 单一职责原则 方法设计的原则

以上是关于java中设计思想及设计原则及设计模式解析的主要内容,如果未能解决你的问题,请参考以下文章

Java设计模式(1:软件架构设计七大原则及开闭原则详解)

01.设计模式分类及原则

设计模式六大原则及综述

java设计原则

设计模式概念原则及分类

AngularJS_01之基础概述设计原则及MVC设计模式