java常见的设计模式

在了解java常见的设计模式之前,我们要了解三个问题,

        1.为什么要了解设计模式?

        2.这些设计模式的是根据什么原则来设计的?

        3.有哪些常见的设计模式?

  • 为什么要了解设计模式?

        因为设计模式代表了最佳的实践,是很多优秀的软件开发人员的经验总结,恰当的使用设计模式可以增强代码的可复用性,可维护性,可扩展性,健壮性及安全性。

  • 这些设计模式的是根据什么原则来设计的?

        这些设计模式的基础其实就是java面向对象,而且大部分的设计原则就是根据oop的设计原则(开闭原则、里氏替换原则、迪米特原则(最少知识原则)、单一职责原则(DIP)、接口分离原则(ISP)、依赖倒置原则、组合/聚合复用原则),目的很简单,就是体现java三大特点,为了更好的体现出程序、代码的灵活性。

  • 有哪些常见的设计模式?

经典的设计模式有23种,此篇文章介绍4种常见的设计模式,单例模式、工厂模式、责任链模式和观察者模式。

1.单例模式

        1.1 概念

        顾名思义,保证在内存中只用一个实例

        1.2 使用场景

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

        1.3 优缺点

        优点:

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

        缺点:

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

        1.4 示例

        饥饿模式

/**
 * 单例模式,饥饿加载
 */
public class SingletonDemo {

    //1. 需要有一个私有的构造函数,防止该类通过new的方式创建实例
    private SingletonDemo(){}

    //2. 饥饿模式,首先生成一个实例
    private static final SingletonDemo instance = new SingletonDemo();

    //3. 静态方法,用于获取已经生成的实例
    public static SingletonDemo getInstance() {
        return instance;
    }

    public String hello(String name) {
        return "hello " + name;
    }

}

这种直线方式简单,在加载类的时候就会创建好实例,等待被调用,且是线程安全的。

        懒汉模式

        第一种写法:

/**
 * 单例模式: 懒汉式
 */
public class SingletonDemo02 {

    private SingletonDemo02(){}

    private static SingletonDemo02 singletonDemo02 = null;

    public static SingletonDemo02 getInstance() {

        if (singletonDemo02 == null) {
            singletonDemo02 = new SingletonDemo02();
        }

        return singletonDemo02;
    }

    public String hello(String name) {
        return "hello " + name;
    }

}

注意: 这种方式在多线程访问时会有问题,具体原因就是如果线程多的时候,由于时间片的轮换机制,可能会导致产生多个实例。

        第二种写法:

/**
 * 单例模式: 懒汉式,线程安全,但性能较低
 */
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) {
            synchronized (SingletonDemo03.class) {
                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) {
            synchronized (SingletonDemo03.class) {
                if(singletonDemo03 == null) {
                    singletonDemo03 = new SingletonDemo03();
                }
            }
        }
        return singletonDemo03;
    }

    public String hello(String name) {
        return "hello " + name;
    }

}

        第五种写法:

        

/**
 * 单例模式: 懒加载, 线程安全
 */
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 String hello(String name) {
        return "hello " + name;
    }

}

        第六种写法:

public enum  SingletonDemo05 {

    INSTANCE;

    public String hello(String name) {
        return "hello " + name;
    }
	
}

可以保证单例,且线程安全,同时也是最简单的,因为枚举类型不能够被实例化,前5种方式还是可以通过java的反射机制获取新的实例,但是枚举类型的不可以,因为一运行,就由java虚拟机jvm实例,别人是示例不了的。

2 工厂模式

        2.1 概念

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

        2.2 使用场景

        为什么需要工作模式,原来使用new的方式感觉也很简单啊,而且也很容易看明白啊。

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

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

        2.3 工厂方法(简单工厂)

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

        类图:

         代码:

          抽象类,鸭子的规范类

public abstract class Duck {

    abstract public void quack();

}

        三个子类

public class RubberDuck extends Duck {

    @Override
    public void quack() {

        System.out.println("我是橡皮鸭,");

    }

}
public class WildDuck extends Duck {

    @Override
    public void quack() {

        System.out.println("我是真鸭子");

    }

}
public class DonaldDuck extends Duck {

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

}

        工厂类

public class DuckFactory {

    private DuckFactory(){}

    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 final int ZH_DUCK = 5;

    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();
            case ZH_DUCK:
                return new ZhouHeiDuck();
            default:
                return null;
        }
    }

        测试类

public class Main {

    public static void main(String[] args) {
        Duck donaldDuck = DuckFactory.getInstance(DuckFactory.DONALD_DUCK);
        donaldDuck.quack();

        Duck wildDuck = DuckFactory.getInstance(DuckFactory.WILD_DUCK);
        wildDuck.quack();
    }

}

        输出结果:

        我是唐老鸭
        我是真鸭子

        好处:只要给定特定的鸭子类型,就可以实例化出对应的实体,这样会利于修改和拓展,体现出更好的灵活性。

        2.4 抽象工厂

        用于生成指定产品族,一个产品族中包括多种产品。例如:
我们都比较熟悉的电脑制造相关行业,有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(惠普)工厂

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();
}

        Hpkeyboard(HP的键盘制作工厂)


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

        LogicKeyboard(Logic的键盘制作工厂)

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

        键盘抽象工厂Mouse

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

         HpMouse(HP的鼠标制作工厂)

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

        LogicMouse (Logic的鼠标制作工厂)

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());
    }
 
}

        结果:

        HP keyboard
        HP mouse

3.责任链模式

        3.1 概念

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

为了便于理解我们可以想象一下击鼓传花的游戏。


        3.2 使用场景

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

        3.3 类图

        

        代码:

        Filter接口

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

        CheckSyntaxFiler(对语法结构进行检查)

        对语法进行检查不能出现”<>”如果出现了用“#”替换

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(敏感词的过滤)

        若出现敏感词汇用”***”替换

public class WordFilter implements Filter {
 
    @Override
    public void doFilter(Message message) {
        String content = message.getContent();
        content = content.replace("嘻嘻", "***");
        content = content.replace("hha", "***");
        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;
    }
 
}

        Message(demo)

package com.zking.patterndemo;
 
public class Message {
 
    private String content;
 
    public String getContent() {
        return content;
    }
 
    public void setContent(String content) {
        this.content = content;
    }
 
    @Override
    public String toString() {
        return "Message{" +
                "content='" + content + '\'' +
                '}';
    }
}

        测试


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

        结果:

        hello,#abc#,***xx***,哈哈哈

4.观察者模式

        4.1 概念

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

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

        4.2 使用场景

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

        4.3 类图

        代码

        Observer(观察者接口)


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

        Nurse(护士)

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

        Docter(医生)


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

        Wife(妻子)

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

        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();
 
    }
}

        结果:

        I am docter, Can I help you?
        I am nurse, Can I help you?
        baby, I am here, Don't worry !

        

        4.4 小结

观察者模式是使用的非常广泛,比如:Listener,Hook,Callback等等,其实都是观察者的一种应用,名称叫法不同而已,思路基本相同。

总结

         这些都是比较继承的设计模式,很多框架都会运用这些设计模式,只有理解了这些设计模式,才能更好的去了解和使用框架

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值