常见的设计模式

目录

一、为什么需要学习设计模式

二、常见的设计模式

1.单例模式

概念:

使用场景

优缺点

单例模式的分类

2.工厂模式 

概念:

使用原因

工厂方法

抽象工厂 

3.责任链模式

概念

使用场景

案例

4.观察者模式(Obsever)

概念

使用场景

案例


一、为什么需要学习设计模式

设计模式(Design pattern)代表了最佳的实践,是很多优秀的软件开发人员的经验总结,是解决特定问题的解决方案。它并不是语法规定,也不拘泥于特定语言。 恰当的使用设计模式可以提高代码的可复用性,可维护性,可扩展性,健壮性及安全性,这些都是系统非常重要的非功能性需求。设计模式的广泛使用起始于1995年,GOF(四人帮)出版的《设计模式:可复用面向对象软件基础》。

二、常见的设计模式

1.单例模式

概念:

保证在内存中只用一个实例;也可以说:一个系统中,一个类只能有一个实例对象

使用场景

比如:系统配置文件的管理,这些配置文件只要使用一个单例对象进行读写即可,系统在其他地方需要使用配置信息时,只要使用该单例对象进行获取就可以了,这样便于统一管理配置信息

优缺点

优点

  • 在内存中只有一个对象,节省内存空间;
  • 避免频繁的创建销毁对象,可以提高性能;
  • 避免对共享资源的多重占用,简化访问;
  • 为整个系统提供一个全局访问点,易于控制

缺点

  • 不适用于变化频繁的对象
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出

单例模式的分类

1.饥饿模式

也叫饿汉模式,当类加载的时候,不管是否使用该实例都会生成。这种直线方式简单,且线程是安全的

/**
 * 单例模式,饥饿加载
 */
public class SingletonDemo {
 
    //1. 需要有一个私有的构造函数,防止该类通过new的方式创建实例(实例=对象)
    private SingletonDemo(){}
 
    //2. 饥饿模式,首先生成一个实例(不管是否使用该实例都会生成)
    private static final SingletonDemo instance = new SingletonDemo();
 
    //3. 静态方法,用于获取已经生成的实例
    public static SingletonDemo getInstance() {
        return instance;
    }
 
    /*
     * 测试多线程下单例模式是否安全
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(SingletonDemo01.getInstance().hashCode());
            }).start();
        }
 
}

2.懒汉模式 

在类加载的时候没有直接实例化,而是调用指定实例方法的时候再进行实例化,这样就能保证不想使用的时候也不会实例化。一般来说比饿汉模式的效率高,但是在懒汉模式中存在着许多多线程的相关问题。

  • 第一种写法(单例模式: 懒汉式, 有线程问题
/**
 * 单例模式: 懒汉式, 有线程问题
 */
public class SingletonDemo02 {
 
    private SingletonDemo02(){
        //模拟构造函数的运行耗时:在运行构造函数前暂停10毫秒
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    private static SingletonDemo02 singletonDemo02 = null;
 
    public static synchronized SingletonDemo02 getInstance() {
 
        if (singletonDemo02 == null) {
 
            singletonDemo02 = new SingletonDemo02();
        }
 
        return singletonDemo02;
    }
 
    /*
     * 测试多线程下单例模式是否安全
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(SingletonDemo02.getInstance().hashCode());
            }).start();
        }
    }
 
}

  • 第二种写法(单例模式: 懒汉式,线程安全,但性能较低
/**
 * 单例模式: 懒汉式,线程安全,但性能较低
 */
public class SingletonDemo03 {
 
    private SingletonDemo03() {
    }
 
    private static SingletonDemo03 singletonDemo03 = null;
 
    public static synchronized SingletonDemo03 getInstance(){
        if(singletonDemo03 == null) {
            singletonDemo03 = new SingletonDemo03();
        }
        return singletonDemo03;
    }
 
    public String hello(String name) {
        return "hello " + name;
    }
 
}

  • 第三种写法(单例模式: 懒汉式 存在问题
public class SingletonDemo03 {
 
    private SingletonDemo03() {
    }
 
    private static SingletonDemo03 singletonDemo03 = null;
 
    public static  SingletonDemo03 getInstance(){
 
        if(singletonDemo03 == null) {
            //当线程运行到这里时如果CPU被别的线程抢走就会引发多线程问题
            synchronized (SingletonDemo03.class) {
                singletonDemo03 = new SingletonDemo03();
            }
        }
 
        return singletonDemo03;
    }
 
}

  • 第四种写法(单例模式: 懒汉式,双重检查单例
/**
 * 单例模式: 懒汉式,双重检查单例
 */
public class SingletonDemo03 {
 
    private SingletonDemo03(){
    }
 
    private static SingletonDemo03 singletonDemo03 = null;
 
    public static  SingletonDemo03 getInstance(){
        //减小同步块,并使用双重检查来保证线程安装
        if(singletonDemo03 == null) {
            synchronized (SingletonDemo03.class) {
                //双重检查
                if(singletonDemo03 == null) {
                    singletonDemo03 = new SingletonDemo03();
                }
            }
        }
        return singletonDemo03;
    }
 
}

  • 第五种写法(单例模式: 懒加载, 线程安全
/**
 * 单例模式: 懒加载, 线程安全
 */
public class SingletonDemo04 {
 
    //阻止外部实例化
    private SingletonDemo04(){
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    //使用静态内部类来实例一个SingletonDemo04对象
    private static class SingletonDemoHolder {
        private final static SingletonDemo04 instance = new SingletonDemo04();
    }
 
    public static SingletonDemo04 getInstance() {
        return SingletonDemoHolder.instance;
    }
 
}
  • 第六种写法(可以保证单例,且线程安全
public enum  SingletonDemo05 {
 
    INSTANCE;
 
    public String hello(String name) {
        return "hello " + name;
    }
	
}

2.工厂模式 

概念:

用于产生对象的方法或者类,称之为工厂。 上面所讲到的单例模式也可以看作为一个特殊的工厂:即一个类中只生产一个实例

使用原因

使用工厂的原因是我们可以通过工厂模式,来集中控制对象的创建过程,这样可以给设计带来更多的灵活性

比如:spring的IOC容器就是工厂模式的经典实现。

工厂方法

用于生产指定系列的对象。已鸭子为例,鸭子有真的鸭子,橡皮鸭,电子玩具鸭等。如何能方便的创建出各种鸭子,并将创建过程控制起来,以便于以后的维护和扩展?

类图

1.抽象鸭子父类:Duck

public abstract class Duck {
 
    abstract public void quack();
 
}

2.鸭子子类:RubberDuck

public class RubberDuck extends Duck {
 
    @Override
    public void quack() {
        System.out.println("我是橡皮鸭,");
    }
 
}

 3.鸭子子类:WildDuck

public class WildDuck extends Duck {
 
    @Override
    public void quack() {
        System.out.println("我是真鸭子");
    }
 
}

4.鸭子子类: DonaldDuck

public class DonaldDuck extends Duck {
 
    @Override
    public void quack() {
        System.out.println("我是唐老鸭");
    }
 
}

5.鸭子工厂(控制鸭子类) 

public class DuckFactory {
 
    //私有化构造方法
    private DuckFactory(){}
 
    //static 饿汉模式实例出一个类型为鸭子工厂类的对象
    private static DuckFactory duckFactory = new DuckFactory();
    
    //定义图中的几种子类的鸭子类,类型用数字区分,可以手动增加
    public static final int WILD_DUCK = 1;
 
    public static final int RUBBER_DUCK = 2;
 
    public static final int DONALD_DUCK = 3;
 
    public static final int PJ_DUCK = 4;
    
    //依据鸭子类型得到鸭子实例的方法
    public static Duck getInstance(int duckType) {
        switch (duckType) {
            case WILD_DUCK:
                //返回直接实例化好的鸭子子类
                return new WildDuck();
            case RUBBER_DUCK:
                return new RubberDuck();
            case DONALD_DUCK:
                return new DonaldDuck();
            case PJ_DUCK:
                return new PJDuck();
            default:
                return null;
        }
    }
 
}

6.测试 

public class Main {
 
    public static void main(String[] args) {
 
        //DuckFactory可以生产鸭子对象
        Duck donaldDuck = DuckFactory.getInstance(DuckFactory.DONALD_DUCK);
        donaldDuck.quack();
 
        Duck wildDuck = DuckFactory.getInstance(DuckFactory.WILD_DUCK);
        wildDuck.quack();
 
        Duck pjDuck = DuckFactory.getInstance(DuckFactory.PJ_DUCK);
        pjDuck.quack();
    }
 
}

抽象工厂 

概念

用于生成指定产品族一个产品族包括多种产品

例如:

我们都比较熟悉的电脑制造相关行业,有HP,罗技,联想,戴尔,近几年华为,小米也进来了,每个生产商生产的电脑又包括鼠标,键盘,屏幕等等配件。此时我们需要使用工厂模式来进行管理不同的产品族,这时使用简单工厂(也有叫作工厂方法的)已经无法满足要求,此时可以使用抽象工厂。

 类图

 抽象工厂就是在简单工厂的基础上,对多个工厂进行抽象,将不同的工厂类型放入抽象工厂

抽象产品族工厂:PcFactory

public abstract class PcFactory {
 
    //制作方法
    public abstract Mouse makeMouse();
    public abstract Keyboard makeKeyboard();
 
    //为得到具体的工厂的方法服务
    private static HpFactory hpFactory = new HpFactory();
    private static LogicFactory logicFactory = new LogicFactory();
    //为得到具体的工厂的方法服务
    public final static int PC_TYPE_HP = 1;
    public final static int PC_TYPE_LG = 2;
 
    /**
     * 得到具体的工厂的方法
     * @param pcType传入表示电脑类型的常数
     * @return 返回PcFactory抽象类:面向抽象编程代替面向具体编程
     */
    public static PcFactory getPcFactory(int pcType) {
        switch (pcType){
            case 1:
                return hpFactory;
            case 2 :
                return logicFactory;
            default:
                return null;
        }
    }
}

子工厂(惠普): HPFactory(继承抽象类PcFactory)

public class HpFactory extends PcFactory {
 
    //返回抽象类:面向抽象编程代替面向具体编程
 
    @Override
    public Mouse makeMouse() {
        return new HpMouse();
    }
 
    @Override
    public Keyboard makeKeyboard() {
        return new HpKeyboard();
    }
 
}

 子工厂(罗技):LogicFactory(继承抽象类PcFactory)

public class LogicFactory extends PcFactory {
 
    @Override
    public Mouse makeMouse() {
        return new LogicMouse();
    }
 
    @Override
    public Keyboard makeKeyboard() {
        return new LogicKeyboard();
    }
}

 键盘抽象工厂:Keyboard

public abstract class Keyboard {
    abstract String getInfo();
}

 HP的键盘制作工厂(继承抽象类Keyboard)

public class HpKeyboard extends Keyboard {
    @Override
    String getInfo() {
        return "HP keyboard";
    }
}

 Logic的键盘制作工厂(继承抽象类Keyboard)

public class LogicKeyboard extends Keyboard {
    @Override
    String getInfo() {
        return "logic keyboard";
    }
}

鼠标抽象工厂Mouse

public abstract class Mouse {
    abstract String getInfo();
}

 HP的鼠标制作工厂(继承抽象类Mouse)

public class HpMouse extends Mouse {
    @Override
    String getInfo() {
        return "HP mouse";
    }
}

 Logic的鼠标制作工厂(继承抽象类Mouse)

public class LogicMouse extends Mouse {
    @Override
    String getInfo() {
        return "logic mouse";
    }
}

 测试类 

public class Main {
 
    public static void main(String[] args) {
        //通过抽象PcFactory父类得到HP电脑制作工厂
        PcFactory HpFactory = PcFactory.getPcFactory(PcFactory.PC_TYPE_HP);
        //得到HP制作键盘的方法
        Keyboard keyboard = HpFactory.makeKeyboard();
        //得到HP制作鼠标的方法
        Mouse mouse = HpFactory.makeMouse();
        System.out.println(keyboard.getInfo());
        System.out.println(mouse.getInfo());
    }
 
}

3.责任链模式

概念

责任链模式是一个对象的行为模式,很多对象之间形成一条链条,处理请求在这个链条上进行传递,直到责任链的上的某个对象决定处理请求(也可扩展为几个对象处理),这个过程对于用户来说是透明的,也就是说用户并不需要知道是责任链上的哪个对象处理的请求,对请求是否处理由链条上的对象自己决定。

可以想象一下击鼓传花的游戏。

责任链模式也可以说是行为模式(而抽象工厂叫做建造模式):拥有相同行为的类共同实现一个接口

使用场景

web容器中的过滤器算是责任链模式的一个经典场景。另外举个例子:当在论坛上提交内容时,论坛系统需要对一些关键词进行处理,看看有没有包含一些敏感词汇,而这些敏感词汇我们可以使用责任链模式进行处理。

案例

类图

从上图中可以看到ChackSyntaxFilter类和WordFilter类共同实现Filter接口 

Filter接口 

/**
 * Filter接口,实际上是对变化的抽象
 * 这种方式会逐个的运行Filter,但不能
 * 指定是否需要继续执行后面的Filter。
 * 比如:当发现违法了特殊符号的Filter时
 * 其后的过滤链没有必要执行
 */
public interface Filter {
 
    void doFilter(Message message);
 
}

ChackSyntaxFilter类(继承Filter接口)

public class ChackSyntaxFilter implements Filter {
 
    @Override
    public void doFilter(Message message) {
        String content = message.getContent();
        content = content.replace("<", "#");
        content = content.replace(">", "#");
        message.setContent(content);
    }
 
}

WordFilter类(继承Filter接口)

public class WordFilter implements Filter {
 
    @Override
    public void doFilter(Message message) {
        String content = message.getContent();
        content = content.replace("牛", "***");
        content = content.replace("马", "*****");
        message.setContent(content);
    }
 
}

 FilterChain过滤器链

/**
 * 将Filter组织成一个链条
 */
public class FilterChain {
 
    private FilterChain(){}
    
    private static List<Filter> filters = new ArrayList<>();
 
    private static FilterChain instance = new FilterChain();
 
    public static FilterChain getInstance(){
        return instance;
    }
 
    public FilterChain add(Filter filter) {
        filters.add(filter);
        return this;
    }
 
    public Message dofilters(final Message message) {
        for (Filter f : filters) {
            f.doFilter(message);
        }
        return message;
    }
 
}

测试 

public class Main {
 
    public static void main(String[] args) {
        Message msg = new Message();
        msg.setContent("hello, <abc>, 牛xx马, 哈哈哈");
 
        FilterChain fc = FilterChain.getInstance();
        fc.add(new ChackSyntaxFilter())
                .add(new WordFilter())
                .dofilters(msg);
 
        System.out.println(msg.getContent());
    }
 
}

4.观察者模式(Obsever)

概念

观察者模式是对象的行为模式,有时也称为“发布/订阅模式”或者“监听器模式”。

观察者模式定义了被观察者和观察者之间的对多的关系,让多个观察者对象可以响应一个被观察者对象。

使用场景

比较经典的使用场景,比如:java中的swing包中对事件的处理。浏览器对鼠标,键盘等事件的处理等, spring中的事件发布机制也是使用该模式

案例

类图 

Nurse类、Wife类、Docter类都共同实现Observer接口,BellEvent按铃类继承Event事件抽象类,Patient是病人实体类

Observer接口

public interface Observer {
    void bell(BellEvent event);
}

Nurse类(继承Observer接口)

public class Nurse implements Observer {
    @Override
    public void bell(BellEvent event) {
        System.out.println("I am nurse, Can I help you?");
    }
}

Wife类(继承Observer接口)

public class Wife implements Observer {
    @Override
    public void bell(BellEvent event) {
        System.out.println("baby, I am here, Don't worry !");
    }
}

Docter类(继承Observer接口)

public class Docter implements Observer {
    @Override
    public void bell(BellEvent event) {
        System.out.println("I am docter, Can I help you?");
    }
}

Event事件抽象类

public abstract class Event {
 
    protected Object source;
 
    public Object getSource() {
        return this.source;
    }
 
}

BellEvent类

public class BellEvent extends Event {
 
    long timestamp;
 
    public BellEvent(Object source) {
        this.timestamp = System.currentTimeMillis();
        this.source = source;
    }
 
}

病人实体类:Patient

public class Patient {
 
    private List<Observer> observers = new ArrayList<>();
 
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
 
    public void ringBell() {
        BellEvent event = new BellEvent(this);
        for (Observer observer: observers) {
            observer.bell(event);
        }
    }
 
}

测试

public class Main {
 
    public static void main(String[] args) {
 
        Patient patient = new Patient();
 
        patient.addObserver(new Docter());
        patient.addObserver(new Nurse());
        patient.addObserver(new Wife());
 
        patient.ringBell();
 
    }
}

常见的设计模式就分享到这了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值