• Java的常见的设计模式解析


    为什么需要学习设计模式

    设计模式(Design pattern)代表了最佳的实践,是很多优秀的软件开发人员的经验总结,是解决特定问题的解决方案。它并不是语法规定,也不拘泥于特定语言。 恰当的使用设计模式可以代码的可复用性,可维护性,可扩展性,健壮性及安全性,这些都是系统非常重要的非功能性需求。

    设计模式的广泛使用起始于1995年,GOF(四人帮)出版的《设计模式:可复用面向对象软件基础》。

    本部分相关的示例代码:

    https://gitee.com/lisenaq/patterndemo.git

    单例模式

    单例模式的概念

    保证在内存中只用一个实例

    使用的场景:

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

    优点和缺点

    优点:

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

    缺点:

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

    代码示例

    饿汉模式

    饿汉模式采用一种简单粗暴的形式,在定义静态属性时,直接实例化了对象

    1. /**
    2. * 单例模式,饥饿加载
    3. */
    4. public class SingletonDemo {
    5. //1. 需要有一个私有的构造函数,防止该类通过new的方式创建实例
    6. private SingletonDemo(){}
    7. //2. 饥饿模式,首先生成一个实例
    8. private static final SingletonDemo instance = new SingletonDemo();
    9. //3. 静态方法,用于获取已经生成的实例
    10. public static SingletonDemo getInstance() {
    11. return instance;
    12. }
    13. public String hello(String name) {
    14. return "hello " + name;
    15. }
    16. }

     这种方式简单,而且线程安全,但是因为是饥饿模式所以在加载的时候会直接生成一个对象,造成资源浪费

    懒汉模式

    1. /**
    2. * 单例模式: 懒汉式
    3. */
    4. public class SingletonDemo02 {
    5. private SingletonDemo02(){}
    6. private static SingletonDemo02 singletonDemo02 = null;
    7. public static SingletonDemo02 getInstance() {
    8. if (singletonDemo02 == null) {
    9. //.....
    10. singletonDemo02 = new SingletonDemo02();
    11. }
    12. return singletonDemo02;
    13. }
    14. public String hello(String name) {
    15. return "hello " + name;
    16. }
    17. }

     注意: 这种方式在多线程访问时会有问题。

    因为运行程序的时候cpu只会运行一个,会造成cpu资源被抢走从而导致断层在这个//。。。。的地方会被停滞,从而造成多次新建对象

     懒汉模式2

    1. /**
    2. * 单例模式: 懒汉式,线程安全,但性能较低
    3. */
    4. public class SingletonDemo03 {
    5. private SingletonDemo03() {
    6. }
    7. private static SingletonDemo03 singletonDemo03 = null;
    8. //同步锁
    9. public static synchronized SingletonDemo03 getInstance(){
    10. if(singletonDemo03 == null) {
    11. singletonDemo03 = new SingletonDemo03();
    12. }
    13. return singletonDemo03;
    14. }
    15. public String hello(String name) {
    16. return "hello " + name;
    17. }
    18. }

      这种方式在多线程访问时安全,但是效率低下,每运行一次线程会加上锁,造成资源浪费

       懒汉模式3

    1. /**
    2. * 单例模式: 懒汉式,双重检查单例
    3. */
    4. public class SingletonDemo03 {
    5. private SingletonDemo03(){
    6. }
    7. private static SingletonDemo03 singletonDemo03 = null;
    8. public static SingletonDemo03 getInstance(){
    9. //减小同步块,并使用双重检查来保证线程安装,可以吗?
    10. if(singletonDemo03 == null) {
    11. synchronized (SingletonDemo03.class) {
    12. if(singletonDemo03 == null) {
    13. singletonDemo03 = new SingletonDemo03();
    14. }
    15. }
    16. }
    17. return singletonDemo03;
    18. }
    19. public String hello(String name) {
    20. return "hello " + name;
    21. }
    22. }

    这种方式在多线程访问时安全,效率可以

    第五种写法

    1. /**
    2. * 单例模式: 懒加载, 线程安全
    3. */
    4. public class SingletonDemo04 {
    5. //阻止外部实例化
    6. private SingletonDemo04(){
    7. try {
    8. Thread.sleep(10);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. }
    13. //使用静态内部类来使用一个SingletonDemo04对象
    14. private static class SingletonDemoHolder {
    15. private final static SingletonDemo04 instance = new SingletonDemo04();
    16. }
    17. public static SingletonDemo04 getInstance() {
    18. return SingletonDemoHolder.instance;
    19. }
    20. public String hello(String name) {
    21. return "hello " + name;
    22. }
    23. }

     可以看到这个和饥饿模式相差不多,为什么说是懒汉模式,因为它是一个静态的内部类,这个比较特殊,因为它在SingletonDemo04加载的时候,静态的内部类不会被调用,只有当使用这个静态的内部类的收才会调用,既保证线程安全,又保证了资源不会浪费

     第六种枚举类

    1. public enum SingletonDemo05 {
    2. INSTANCE;
    3. public String hello(String name) {
    4. return "hello " + name;
    5. }
    6. }

    因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。

    工厂模式

    介绍

    这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

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

    定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

    为什么要使用工厂模式

    为什么需要工厂模式,原来使用new的方式感觉也很简单,且好懂?

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

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

    使用方法 

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

    类图

     

     

     抽象类 PcFactory 

    1. public abstract class PcFactory {
    2. //制作方法
    3. public abstract Mouse makeMouse();
    4. public abstract Keyboard makeKeyboard();
    5. //为得到具体的工厂的方法服务
    6. private static HpFactory hpFactory = new HpFactory();
    7. private static LogicFactory logicFactory = new LogicFactory();
    8. //为得到具体的工厂的方法服务
    9. public final static int PC_TYPE_HP = 1;
    10. public final static int PC_TYPE_LG = 2;
    11. /**
    12. * 得到具体的工厂的方法
    13. * @param pcType传入表示电脑类型的常数
    14. * @return 返回PcFactory抽象类:面向抽象编程代替面向具体编程
    15. */
    16. public static PcFactory getPcFactory(int pcType) {
    17. switch (pcType){
    18. case 1:
    19. return hpFactory;
    20. case 2 :
    21. return logicFactory;
    22. default:
    23. return null;
    24. }
    25. }
    26. }

    惠普工厂 HpFactory

    1. public class HpFactory extends PcFactory {
    2. //返回抽象类:面向抽象编程代替面向具体编程
    3. @Override
    4. public Mouse makeMouse() {
    5. return new HpMouse();
    6. }
    7. @Override
    8. public Keyboard makeKeyboard() {
    9. return new HpKeyboard();
    10. }
    11. }

     罗技工厂 LogicFactory

    1. public class LogicFactory extends PcFactory {
    2. @Override
    3. public Mouse makeMouse() {
    4. return new LogicMouse();
    5. }
    6. @Override
    7. public Keyboard makeKeyboard() {
    8. return new LogicKeyboard();
    9. }
    10. }

     键盘抽象类 Keyboard

    1. public abstract class Keyboard {
    2. abstract String getInfo();
    3. }

     惠普键盘类 HpKeyboard

    1. public class HpKeyboard extends Keyboard {
    2. @Override
    3. String getInfo() {
    4. return "HP keyboard";
    5. }
    6. }

     罗技键盘类 LogicKeyboard 

    1. public class LogicKeyboard extends Keyboard {
    2. @Override
    3. String getInfo() {
    4. return "logic keyboard";
    5. }
    6. }

     鼠标抽象类 Mouse

    1. public abstract class Mouse {
    2. abstract String getInfo();
    3. }

     惠普鼠标类 HpMouse 

    1. public class HpMouse extends Mouse {
    2. @Override
    3. String getInfo() {
    4. return "HP mouse";
    5. }
    6. }

     罗技鼠标类 LogicMouse

    1. public class LogicMouse extends Mouse {
    2. @Override
    3. String getInfo() {
    4. return "logic mouse";
    5. }
    6. }

     测试类

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

     责任链模式

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

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

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

     类图 

     

     Filter 接口

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

     ChackSyntaxFilter类

    1. public class ChackSyntaxFilter implements Filter {
    2. @Override
    3. public void doFilter(Message message) {
    4. String content = message.getContent();
    5. content = content.replace("<", "#");
    6. content = content.replace(">", "#");
    7. message.setContent(content);
    8. }
    9. }

     WordFilter类

    1. public class WordFilter implements Filter {
    2. @Override
    3. public void doFilter(Message message) {
    4. String content = message.getContent();
    5. content = content.replace("牛", "***");
    6. content = content.replace("马", "*****");
    7. message.setContent(content);
    8. }
    9. }

      FilterChain过滤器链

    1. /**
    2. * 将Filter组织成一个链条
    3. */
    4. public class FilterChain {
    5. private FilterChain(){}
    6. private static List<Filter> filters = new ArrayList<>();
    7. private static FilterChain instance = new FilterChain();
    8. public static FilterChain getInstance(){
    9. return instance;
    10. }
    11. public FilterChain add(Filter filter) {
    12. filters.add(filter);
    13. return this;
    14. }
    15. public Message dofilters(final Message message) {
    16. for (Filter f : filters) {
    17. f.doFilter(message);
    18. }
    19. return message;
    20. }
    21. }

     测试 

    1. public class Main {
    2. public static void main(String[] args) {
    3. Message msg = new Message();
    4. msg.setContent("hello, <abc>, 牛xx马, 哈哈哈");
    5. FilterChain fc = FilterChain.getInstance();
    6. fc.add(new ChackSyntaxFilter())
    7. .add(new WordFilter())
    8. .dofilters(msg);
    9. System.out.println(msg.getContent());
    10. }
    11. }

     观察者模式

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

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

     类图

      从上图可以看到:Nurse类、Wife类、Docter类都共同实现Observer接口,BellEvent按铃类继承Event事件抽象类,Patient是病人实体类

    Observer接口 

    1. public interface Observer {
    2. void bell(BellEvent event);
    3. }

     Nurse类

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

     Wife类

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

     Doctor类

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

     Event抽象类 

    1. public abstract class Event {
    2. protected Object source;
    3. public Object getSource() {
    4. return this.source;
    5. }
    6. }

     BellEvent类

    1. public class BellEvent extends Event {
    2. long timestamp;
    3. public BellEvent(Object source) {
    4. this.timestamp = System.currentTimeMillis();
    5. this.source = source;
    6. }
    7. }

     病人实体类

    1. public class Patient {
    2. private List<Observer> observers = new ArrayList<>();
    3. public void addObserver(Observer observer) {
    4. observers.add(observer);
    5. }
    6. public void ringBell() {
    7. BellEvent event = new BellEvent(this);
    8. for (Observer observer: observers) {
    9. observer.bell(event);
    10. }
    11. }
    12. }

     测试

    1. public class Main {
    2. public static void main(String[] args) {
    3. Patient patient = new Patient();
    4. patient.addObserver(new Docter());
    5. patient.addObserver(new Nurse());
    6. patient.addObserver(new Wife());
    7. patient.ringBell();
    8. }
    9. }
  • 相关阅读:
    大数据技术基础实验五:Zookeeper实验——部署ZooKeeper
    Android 9.0 蓝牙功能之一:蓝牙电话
    SpringBootWeb项目启动时报错端口号被占,又找不到哪个项目启动后没关,解决办法如下:
    协程是如何实现线程切换的
    根据表单控件名称,获取对应指定字段名称
    Toronto Research Chemicals萜烯分析丨反式植物醇
    河南分销小程序开发|三级分销玩法介绍
    HCIA-HarmonyOS Application Developer 课程大纲
    线程池之ThreadPoolExecutor
    【RCNN系列】RCNN论文总结
  • 原文地址:https://blog.csdn.net/WZJ278/article/details/125405078