常见的设计模式-创建型模式(5种)

xiaoxiao2021-02-28  69

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结使用 设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样

看过好多关于设计模式的文章,这篇还是不错的,讲的很详细。

从定义上就能看出设计模式的重要性和实用性。在代码中使用合适的设计模式可以在提升代码结构合理性,降低耦合度。常用的设计模式有23种,这里只列举出目前用到过的,其他的以后再补充。

1.单例模式

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。

从单例模式的定义也能看出他的大概使用场景,它可以用来控制实例的数量。

注意,因为这个模式本身并不难,而且用起来会省去大量的创建实例代码,所以项目中会经常用到。 但是一定要注意使用场景,不然会出现很多意想不到的问题。

写网络请求框架的时候注意,并发请求的时候,使用单例会造成结果异常。创建Fragment 的时候使用单例,会造成内存泄露。

单例模式的写法 1.普通写法 -私有化构造方法,保证只有在类内部才能创建实例 -定义一个私有的实例变量 -向外提供一个创建实例的方法

public class BaseSingleton { private static BaseSingleton mInstance; //私有化构造方法,不让在外部直接创建对象 private BaseSingleton(){ } //提供一个公有的方法提供对象实例 public static BaseSingleton getInstance() { if (mInstance==null) { mInstance=new BaseSingleton(); } return mInstance; } }

上述代码直接用BaseSingleton.getInstance()即可创建实例,但是在多线程的情况下是不安全的。可能会创建多个实例,违背了该模式的初衷。因此需要改进. 改进1: 在getInstance()方法上直接加锁,可以解决线程安全问题

/**为解决线程安全问题,直接在方法上加synchronized * 1.每次调用getInstance都要对对象上锁,造成性能打折 * 2.我们只需在第一次调用的时候加锁即可 * @return */ public synchronized BaseSingleton getInstance2() { if (mInstance==null) { mInstance=new BaseSingleton(); } return mInstance; }

这种解决办法造成的问题也在注释上说明了,因为每次调用getInstance()方法,不管是不是已经创建过对象,都会加锁(理论上我们只需要在第一次创建对象的时候加锁即可),所以会降低性能,并不是一个合理的方案。 继续改进,既然不能直接在创建方法上添加synchronized,那么我们就在第一次创建对象的时候加锁。

/**新问题 * 1.java指令中创建对象和赋值操作是分开进行的,也就是说mInstance=new BaseSingleton();语句是分两步执行的 * 但是jvm并不能保证这两个操作的执行顺序,也就是说JVm有可能先给实例分配空间,然后直接赋值给mInstance,然后再去初始化Singleton实例, * 这样就可能出错 * * 我们以A、B两个线程为例: a>A、B线程同时进入了第一个if判断 b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton(); c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。 d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。 e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。 * @return */ public synchronized BaseSingleton getInstance3() { if (mInstance==null) { synchronized (mInstance) { if (mInstance==null) { mInstance=new BaseSingleton(); } } } return mInstance; }

此种方法可以解决在方法上加锁的性能问题,但是也造成了新的问题。由于java机制的问题,对象的创建和赋值是分开进行的,因此不能保证返回的mInstance一定是有值的。有可能在调用getInstance的时候直接返回一个null,这就不能让人接受了。具体分析过程看注释。 那么我们怎么解决这个问题呢? 由于JVM机制可以保证类的加载过程是互斥的,也就是说JVM可以保证每个类都只加载一次。 所以我们可以使用内部类的形式来维护一个实例

/**通过内部类来维护单例的实现 * 1.JVM内部的机制能够保证当一个类在加载的时候,这个类的加载过程是互斥的。 * 2.当我们第一次调用getInstance4()的时候,JVm能够帮我们保证instance制备实例化一次, * 3.并保证把赋值给instance 的内存初始化完毕 * @author WHH * */ private static class SingletonFactory{ private static BaseSingleton mInstance=new BaseSingleton(); } public static BaseSingleton getInstance4() { return SingletonFactory.mInstance; }

通过这种方式就可以创建出一个相对完美的单例了。 推荐使用这个


2.工厂模式

这里就不贴百度的定义了,这个模式有好几个细分的模式,简单工厂模式、工厂方法模式、静态工厂方法模式、抽象工厂方法模式等。这么一大堆的工厂模式到底该怎么区分呢?我是这么记忆的,简单工厂模式当然是最基础的了,最基础的当然也会是最简单的,那么什么是简单工厂模式呢?

简单工厂模式

-直接在工厂类中根据传入的type,创建不同的实例,就是简单工厂模式

这种模式有几个缺点:

如果type写错了会造成很大的影响。要向创建实例,必须先创建工厂类对象。 //定义发送信息的接口 public interface ISender { public void send(String content); } //发送邮件实现类 public class MailSender implements ISender { @Override public void send(String content) { System.out.println("-------->发送邮件:"+content); } } /**发送短信类,实现了ISender接口,重写send逻辑 * @author WHH * */ public class SmsSender implements ISender { @Override public void send(String content) { System.out.println("------------发送短信:"+content); } } //工厂类 public class SendFactory { /**普通工厂模式: * 1.定义公共接口 * 2.定义公共接口的实现类,并重写具体逻辑 * 3.定义工厂类,根据传入的类型创建对应的实体类对象. * @param type * @return */ public ISender createSender(String type) { if ("sms".equals(type)) { return new SmsSender(); }else if ("mail".equals(type)) { return new MailSender(); }else { System.out.println("请输入正确的类型!"); return null; } } } //测试类,传入不同的type创建不同的实例 public class FactoryTest { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("--------------------普通工厂模式-------------------"); SendFactory sendFactory=new SendFactory(); ISender sender=sendFactory.createSender("sms"); sender.send("----------------->发送短信:这是普通工厂模式"); sender=sendFactory.createSender("mail"); sender.send("----------------->发送邮件:这是普通工厂模式"); } }

工厂方法模式

为了解决简单工厂方法传入变量的问题,对工厂方法加以改进就成了工厂方法。把创建实例的方法分开,每一种实例的创建都有对应的一个方法,这样就形成了工厂方法模式

/**工厂方法模式 * 就是把普通工厂方法模式需要传入类型,来创建对应类型对象的模式, * 改成了直接调用对应的方法就可以创建对应的对象。 * 1.比普通工厂看起来更清晰 * 2.无需传参,不用担心参数传错 * @author WHH * */ public class MultiSendFactory { public ISender createSmsSender() { return new SmsSender(); } public ISender createMailSender() { return new MailSender(); } }

//创建实例的时候只需调用

System.out.println("--------------------工厂方法模式-------------------"); MultiSendFactory multiSendFactory=new MultiSendFactory(); ISender smsSender=multiSendFactory.createSmsSender(); smsSender.send("------------->发送短信:工厂方法模式"); smsSender=multiSendFactory.createMailSender(); smsSender.send("------------->发送邮件:工厂方法模式");

每次创建实例都要先创建工厂对象,很麻烦是吧?不要急,接下来就用到了静态工厂方法模式 3.静态工厂方法模式 解决了工厂方法模式创建繁琐的问题

/**静态工厂方法模式 * 1.就是把多工厂方法模式的创建方法用static修饰。 * 2.调用更加简单,static修饰的方法可以直接使用类名.调用。 * @author WHH * */ public class StaticFactory { public static ISender createSmsSender() { return new SmsSender(); } public static ISender createMailSender() { return new MailSender(); } }

4.抽象工厂模式 这个模式其实也不难理解,说白了就是把工厂也给抽出来了,抽成了一个专门的创建工厂的接口,目的是为了解决上述几种工厂方法的扩展性问题。不能每次增加新类都去修改Factory类吧。所以就把工厂类也给抽象出来了。这样的话如果需要新增工厂类,只需要实现工厂接口,重写其中的方法即可。

//工厂接口 public interface IFactory { public ISender createSenderFactory(); } //发送短信的工厂类 public class SmsFactory implements IFactory { @Override public ISender createSenderFactory() { // TODO Auto-generated method stub return new SmsSender(); } } //发送邮件的工厂类 public class MailFactory implements IFactory { @Override public ISender createSenderFactory() { // TODO Auto-generated method stub return new MailSender(); } }

3.建造者模式

建造者模式可以用来创建一些有复杂逻辑的对象,比如说如果某个对象的一些方法必须按照特定的顺序执行,就可以采用建造者模式。通过独有的Builder类创建对象,用户无需担心方法的调用顺序,所有的一切都已经封装在了Builder类中。

使用步骤:

私有化构造方法,避免用户在外部直接创建对象,造成方法调用混乱创建静态内部类Builder,Builder类需要向外提供相应属性方法的入口。创建build方法,在该方法内实现外部类对象的创建,并确定外部类方法的调用顺序,并返回外部类对象.

举个栗子:

比如我们要创建一个Dialog类,必须要先执行initView方法来初始化各种组件,然后在调用setData方法填充数据,最后在调用show方法来显示。注意这里方法的调用顺序,必须先调用initView(),再嗲用setData(),不然会报空指针异常。所以我们这里就可以使用建造者模式来实现,代码如下

package com.hank.ok.dialogactivity; import android.content.Context; /** * 类功能描述:建造者模式 * author:WHH * create time: 2017-09-06 10:27 * version:${version} */ public class ReportDialog { private Context mContext; private ReportDialog(Context mContext) { this.mContext = mContext; } private void initView() { //初始化View } private void setData() { //填充数据 } public void show(){ //显示 } public static class Build { private Context mContext; private String mTitle; private String mContent; public Build(Context mContext) { this.mContext = mContext; } public Build setTitle(String title) { this.mTitle = title; return this; } public Build setContent(String content) { this.mContent = content; return this; } public ReportDialog build() { ReportDialog dialog = new ReportDialog(mContext); dialog.initView(); dialog.setData(); return dialog; } } }
转载请注明原文地址: https://www.6miu.com/read-47294.html

最新回复(0)