JAVA设计模式--创建模式

it2023-04-17  81

学习设计模式的目的

其实学习设计模式的通俗的目的来说就是偷懒,但是这个偷懒是有技巧的,而设计模式就是实现这些偷懒技巧的具体思想,设计模式的最终目的就两点:

1、 写出面向对象编程下的高质量的代码,减少给以后业务的发展和修改带来技术债的风险 2、 隔离变化,提高代码复用率,为以后的修改提供一种良性的支持

用官方的说法就是写出高内聚、低耦合、易复用的代码(也称为OOP的方法论)。

设计模式学习的前提

学习设计模式前我们首先要掌握两点:

1、 oop语言的基本特性,以java来说就是封装、继承和多态(当然包括类、对象、接口这些东西了) 2、 oop语言的一些设计原则,包含(开闭原则、单一职责原则、依赖倒置原则、里氏替换原则、单一职责原则、迪米特原则或最少知识原则、接口隔离原则)

这两点中呢,oop基础特征是我们在实现这些思想的工具,而设计原则就是指导思想,最后设计模式呢是结果,也就是前辈们在开发过程总结的一些针对不同场景,产生的一些实用性的框架。 oop的的设计原则的定义呢网上有很多解释,用我觉得通俗一些的话来说呢就是以下这些:

1.类的设计要尽可能的小,也就是我们说的迪米特原则,在做电商时设计商品的spu和sku时这一点就体现得很明确,通过多层的一个主从关系,每个主从关系只负责一小块儿,最终就实现了一个复杂的商品的详情了,且每个商品的详情都能这样去描述 2.面向接口编程 ,这个不用说了,隔离变化的的主要方式 3.方法的设计原则,单一职责,就是一个方法只干一件事儿,这样逻辑清晰,可替换和复用性强 4.找出变化的,隔离不变的,偷懒的主要来源,哈哈 5.对修改关闭,对扩展开放,开闭原则,常用的策略,装饰啊,代理啊,都是这类思想的体现 6.依赖抽象类,不要依赖具体类,这个就是抽象度的问题了,比如ORM框架的底层的JDBC的泛型都是T,最大程度的复用了代码 7.一个接口的设计,应该都是相关且尽量小的,这就是接口隔离原则

掌握了这些之后呢,我们就可以开始偷懒了,去实现一些具体的偷懒的框架了

设计模式内容

说到设计模式的内容啊,也就是我们偷懒的框架了,我们又要想了,一个简单的项目,我们随便怎么写都可以,但是一个复杂的项目怎么去偷懒呢,这个时候,设计模式也有分类了,设计模式呢总共分为三大类,二十三小种,其中一些思想还比较接近只是适用于不同场景。那说到分类,我们怎么去分的呢,其实就是从小往大的说,就是按照类的创建,行为的划分和结构的划分分为:

1.创建模式(5种):创建模式关注的是类的创建,重点是将类对象的创建与使用分离,这样能降低系统耦合度。 2.行为模式(11种):行为模式关注的是业务的流程,将业务中产生的类,对象按照某种特定的关系完成一个具体的职责,用来协调类与对象一起去完成一个复杂的过程。 3.结构模式(7种):结构模式关注的是代码本身的结构,能让代码以更大的结构持续良好的运行下去,且能实现代码的复用,以及降低系统耦合度。

创建模式

单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。

单例模式

单例模式其实就是全局只有唯一的一个实例,并提供给你单一的访问方式,能节省系统的内存,分为两种模式:饿汉模式(预加载)、懒汉模式(延迟加载),spring的IOC容器默认类的实例就是单例模式

饿汉模式(线程安全可用)

/** * 单例模式的实现其实很简单,就是构造方法私有化,然后提供一个静态方法给你去获取实例 * 而静态方法就保证只给你同一个实例就ok了 * * 饿汉模式(其实就是预加载): * 缺点:该实例还没用到就被加载进内存了,造成了内存的浪费 * 优点:没有多线程的问题,多线程情况下也是单例的 */ public class SingletonHungry { /** * 构造方法私有化,无法通过构造方法实例化对象 */ private SingletonHungry(){} /** * 静态私有成员变量,在代码加载进内存时就会产生对象实例 */ private static final SingletonHungry singletonHungry = new SingletonHungry(); /** * 访问该实例的唯一的全局的方法 * @return */ public static SingletonHungry getInstance(){ return singletonHungry; }

懒汉模式(线程不安全不可用)

/** * 单例模式的懒汉模式(延迟加载) */ public class SingletonLazy { // 私有化构造方法 private SingletonLazy(){} private static SingletonLazy singletonLazy = null; // 线程不安全的获取实例的方式 public static SingletonLazy getInstance(){ // 不安全的主要原因就是这个判断和创建实例是两条指令不是一条指令 if(null == singletonLazy){ singletonLazy = new SingletonLazy(); } return singletonLazy; } }

懒汉模式(方法加锁,线程安全可用)

/** * 单例模式的懒汉模式(延迟加载) * 方法加锁,线程安全,但是呢每个获取实例都排队,性能一般 */ public class SingletonLazyWithSync { // 私有化构造方法 private SingletonLazyWithSync(){} private static SingletonLazyWithSync singletonLazyWithSync = null; /** * 方法加锁,线程安全,但是呢每个获取实例都排队,性能一般 * @return */ public static synchronized SingletonLazyWithSync getInstance(){ if(null == singletonLazyWithSync){ singletonLazyWithSync = new SingletonLazyWithSync(); } return singletonLazyWithSync; }

懒汉模式(锁粒度细化,线程安全可用,性能更优)

/** * 懒汉模式,最终版本 */ public class SingletonLazyFinal { /** * volatile关键字一定不能省略,及时刷新回主内存 */ private volatile static SingletonLazyFinal singletonLazyFinal = null; private SingletonLazyFinal(){} public static SingletonLazyFinal getInstance(){ // 为啥不直接在判断外面加锁呢,也是为了效率问题,不然跟方法上加锁相差不大 if(null == singletonLazyFinal){ synchronized (SingletonLazyFinal.class){ // 为啥多加一次判断呢 因为该线程获取锁之前已经过了上一次的判断的指令了,这时候可能该类已经被实例化了 if (null == singletonLazyFinal){ singletonLazyFinal = new SingletonLazyFinal(); } } } return singletonLazyFinal; } }

综述,单例模式的主要目的是减少类的实例和创建,通常在你要去做一个基础框架程序的时候,可以躲考虑是否采用,正常情况下,面向对象程序都有这种框架的,比如JAVA的spring。而使用单例模式的一般都是采取以上方式第一种或最后一种。

工厂模式

简单工厂模式

/** * 简单工厂 * 一个工厂负责产生所有实例 * 问题: * 类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则 * 解决办法: * 采用配置的方式去对应实例,这样代码就不用修改了,但是又产生了新的问题,不存的类型无法创建会抛出异常 */ public class SimpleDrinksFactory { public static Drinks createDrinks(String type) throws Exception { if("cola".equals(type)){ return new ColaDrinks(); } if ("Juice".equals(type)){ return new JuiceDrinks(); }else{ throw new Exception("该工厂暂时不生成该类饮品!"); } } public static void main(String[] args) throws Exception { Drinks drinks = SimpleDrinksFactory.createDrinks("cola"); drinks.isDrink(); } }

工厂方法模式

/** * 工厂方法模式 * 由具体的工厂去实现具体的产品实例的生成 * 工厂方法的元素有: * 1.抽象工厂接口 * 2.各个具体工厂的接口实现 * 3.抽象产品接口 * 4.各个具体产品的接口实现 */ public interface DrinksFactory { public Drinks createDrink(); } /** * 可乐工厂 * 生产可乐 */ public class ColaDrinksFactory implements DrinksFactory { @Override public Drinks createDrink() { return new ColaDrinks(); } } /** * 果汁工厂 */ public class JuiceDrinksFactory implements DrinksFactory { @Override public Drinks createDrink() { return new JuiceDrinks(); } } /** * 饮料父类 */ public abstract class Drinks { public String name; public abstract void isDrink(); } /** * 可乐 */ public class ColaDrinks extends Drinks { @Override public void isDrink() { System.out.println("I am Cola"); } public ColaDrinks(){ this.name = "可乐"; } } /** * 果汁 */ public class JuiceDrinks extends Drinks { @Override public void isDrink() { System.out.println("I am Juice"); } public JuiceDrinks(){ this.name = "果汁"; } }

静态工厂方法模式

静态工厂方法其实不需要去定义父类接口,各子类将创建的行为直接定义为静态方法就好

抽象工厂模式

工厂模式其实就是抽象工厂模式的一种特例,就是一个工厂只产生一类产品。如果工厂有多组产品的时候,工厂方法就需要再转换一下,多加个生产其他产品的创建行为了。

综上,工厂方法适用于将实例的创建的逻辑与实例的方法调用的逻辑分开的时候使用,当if判断逻辑少的时候可以使用简单工厂方法,同时可以引入配置,去掉if判断;当具体产品多的时候,一直新增if判断也不太好,这时候就可以使用工厂方法模式了,这时候新增产品,不用去修改工厂类了,直接新增具体工厂和具体产品就好了,当然也可以采用静态工厂方法模式;当工厂继续成长,有了新的产品组的时候,就需要考虑使用抽象工厂方法了,将各组的实例的创建行为抽象出来,一个具体的工厂就不是只创建一组产品了。所以工厂方法常常配合策略模式去使用,解决一些实际问题,例如促销方案种经常就使用工厂加策略的方式去实现复杂的计算。

建造者模式

创建者模式有四个角色: 1.产品角色 2.抽象创建者 3.具体创建者 4.面向调用者的项目指挥者

1.产品角色

/** * 产品角色(Product) * 他是一个复杂对象,包含多个产品部分 * 例如手机 * 就有:cpu/电池/屏幕/扬声器/等多个部分组成 */ public class PhoneProduct { // cpu private String cpu; // 电池 private String battery; // 屏幕 private String screen; // 扬声器 private String speaker; public void show(){ System.out.println("这个手机的核心部件cpu是:"+ cpu); System.out.println("这个手机的核心部件battery是:"+ battery); System.out.println("这个手机的核心部件screen是:"+ screen); System.out.println("这个手机的核心部件speaker是:"+ speaker); } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getBattery() { return battery; } public void setBattery(String battery) { this.battery = battery; } public String getScreen() { return screen; } public void setScreen(String screen) { this.screen = screen; } public String getSpeaker() { return speaker; } public void setSpeaker(String speaker) { this.speaker = speaker; } }

2.抽象创建者

/** * 抽象建造者: * 包含:1.手机产品 * 2.返回最终产品结果的方法 * 3.以及手机各个部分的构建的抽象方法 */ public abstract class PhoneBuilder { protected PhoneProduct phoneProduct = new PhoneProduct(); abstract void buildCpu(); abstract void buildBattery(); abstract void buildScreen(); abstract void buildSpeaker(); public PhoneProduct getPhoneProduct() { return phoneProduct; } }

3.具体创建者

/** * 具体建造者,组装各个部分 */ public class HuaweiPhoneBuilder extends PhoneBuilder{ @Override void buildCpu() { phoneProduct.setCpu("麒麟9000"); } @Override void buildBattery() { phoneProduct.setBattery("4500毫安大电池"); } @Override void buildScreen() { phoneProduct.setScreen("京东方柔性曲面屏"); } @Override void buildSpeaker() { phoneProduct.setSpeaker("双扬声器"); } } /** * 具体建造者,组装各个部分 */ public class XiaomiPhoneBuilder extends PhoneBuilder{ @Override void buildCpu() { phoneProduct.setCpu("骁龙865"); } @Override void buildBattery() { phoneProduct.setBattery("4200毫安大电池"); } @Override void buildScreen() { phoneProduct.setScreen("三星120赫兹高刷屏"); } @Override void buildSpeaker() { phoneProduct.setSpeaker("双扬声器"); } }

4.面向调用者的项目指挥者

/** * 项目组织者或者说协调者 */ public class PhoneManager { private PhoneBuilder phoneBuilder; public PhoneManager(PhoneBuilder phoneBuilder){ this.phoneBuilder = phoneBuilder; } public PhoneProduct decorator(){ phoneBuilder.buildCpu(); phoneBuilder.buildBattery(); phoneBuilder.buildScreen(); phoneBuilder.buildSpeaker(); return phoneBuilder.getPhoneProduct(); } public static void main(String[] args){ PhoneBuilder phoneBuilder = new HuaweiPhoneBuilder(); PhoneManager phoneManager = new PhoneManager(phoneBuilder); PhoneProduct phoneProduct = phoneManager.decorator(); phoneProduct.show(); System.out.println("----------------------------------------------"); PhoneBuilder phoneBuilder1 = new XiaomiPhoneBuilder(); PhoneManager phoneManager1 = new PhoneManager(phoneBuilder1); PhoneProduct phoneProduct1 = phoneManager1.decorator(); phoneProduct1.show(); } }

综上,建造者模式重点关注的是大对象的创建过程的解耦,要注意是大对象,它跟工厂模式比较像,但是工厂模式是屏蔽对象的创建,而建造者模式更关注的是对象的各个部分的建造过程,不同顺序的调用,也有可能产生的是不同的对象。

原型模式

原型模式的本质其实就是在大型系统种,很多相同对象或者相似对象,而我们直接调用构造方法去创建这些对象的话,相对来说效率比较差,基于这种情况,原型模式就是基于已存在的对象去创建出一个新的对象,加快效率,而在这个基础上,原型模式又分为:

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。

/** * 原型模式 */ public class OrginalPrototype implements Cloneable { public OrginalPrototype(){ System.out.println("这是原型模式种被克隆的类的创建过程!"); } @Override protected Object clone() throws CloneNotSupportedException { System.out.println("如果需要自定义克隆方法,就在这里加逻辑就好了"); return super.clone(); } public static void main (String[] args)throws CloneNotSupportedException{ OrginalPrototype orginalPrototype = new OrginalPrototype(); OrginalPrototype orginalPrototype1 = (OrginalPrototype)orginalPrototype.clone(); System.out.println(orginalPrototype == orginalPrototype1); } }

由于现代java开发都采用Spring作为基础框架,而spring本身就提供IOC和DI来管理对象的创建和属性的依赖注入,并且默认产生的实例都是单实例,所以原型模式几乎很少就被考虑了,并且原型模式的浅克隆本身也会带来线程的安全问题,所以,如果不是一个需要大批量频繁创建相同对象的情况下,几乎不用考虑原型模式,而且这种模式带来的性能提升在面向对象的编程中来说不算高,java本身的浅克隆方法其实也是基于二进制复制来产生的,所以几乎可以不考虑这种模式。也许复杂模式,比如建造者产生的结果对象属于超大型对象,或许这种也可以考虑下,其他情况下,我个人觉得,几乎可以忽略。

总结

本篇文章虽然为JAVA设计模式,但是因为这个是前辈们写代码的经验,所以很多其他语言也是能使用的,而且这个是个框架,并不是具体实现,所以应用范围更大。另外,设计模式虽然好,但是也不是说所有的代码必须要用到设计模式,在合理的场景下使用合适的设计模式,能事半功倍,而过度设计的话,可能就是事倍功半了。

最新回复(0)