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

本文详细介绍了创建型设计模式,包括单例模式、工厂模式、建造者模式的原理和应用。单例模式确保类只有一个实例,常用于网络请求和管理资源。工厂模式简化对象创建,分为简单工厂、工厂方法和静态工厂方法模式。建造者模式则关注对象构建的顺序,适用于有特定顺序要求的对象创建。文章通过实例代码展示了每种模式的实现和注意事项。
摘要由CSDN通过智能技术生成

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

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

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

1.单例模式

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

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

    1. 写网络请求框架的时候注意,并发请求的时候,使用单例会造成结果异常。
    2. 创建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类中。
使用步骤:
  1. 私有化构造方法,避免用户在外部直接创建对象,造成方法调用混乱
  2. 创建静态内部类Builder,Builder类需要向外提供相应属性方法的入口。
  3. 创建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;
        }

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值