• 面向对象设计模式


    优势


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

    单例模式

    概念:
    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建(即一个单例类在内存中只有一个单例对象)。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

    使用场景

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

    优点

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

    缺点

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

    饿汉模式

    也叫饥饿模式,实质是不管是否被使用该实例都会生成实例。简单,且线程是安全的,但内存占用较多

    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. }

    懒汉模式

    也叫懒加载,实质是只有在需要使用实例时才生成实例。在懒汉模式中存在着许多多线程的相关问题,线程问题不可忽视。

     懒加载有许多种写法根据难易程度列举常见四种线程安全写法

    方法1(给方法上锁)

    这种方法相对较慢,因为需要给每个实例化出来的对象上锁,相对来说比较繁琐

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

    方法2(当类被加载出来时上锁)
    这种方法又叫双重检查,这种写法通过在上锁前后都进行非空判断,方式减少了synchronized 锁资源的消耗,确保了线程的安全

    1. public class SingletonDemo03 {
    2. private SingletonDemo03() {
    3. }
    4. private static SingletonDemo03 singletonDemo03 = null;
    5. public static SingletonDemo03 getInstance(){
    6. if(singletonDemo03 == null) {
    7. //当线程运行到这里时如果CPU被别的线程抢走就会引发多线程问题
    8. synchronized (SingletonDemo03.class) {
    9. singletonDemo03 = new SingletonDemo03();
    10. }
    11. }
    12. return singletonDemo03;
    13. }
    14. }

     方法3(使用jvm虚拟机特性)

    在类中创建内部类,使用内部类的特性,当需要使用内部类的方法或者属性时,会加载外部类,而当外部类被加载出来时,并不会加载内部类。当内部类被static关键字修饰,变成静态内部类,当外部类被反复加载创建时,静态内部类始终只会被加载一次。

    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. }

    方法4(枚举) 

    jvm中的enum(枚举型),enum类中无构造函数,因为它把构造函数交给了jvm虚拟机去初始化,INSTANCE关键字的意思是允许enum类实例,enum类天生就是单例模式,方法默认static,而且java中的反射机制是无法穿透enum的

     

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

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

    用于生产指定系列的对象

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

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

    这里举一个经典的例子

    以鸭子为例,鸭子的类型有很多,真的鸭子,橡皮鸭,电子玩具鸭等。

    如下图,Duck是抽象的鸭子类,是所有类型鸭子的父类,每种类型的鸭子都需要继承父类并重写父类中的方法,这里体现了OOP中的多态
     

     

    抽象父类 鸭子Duck

    1. public abstract class Duck {
    2. abstract public void quack();
    3. }

    橡皮鸭,继承鸭子抽象类

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

    野鸭,继承鸭子抽象类

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

    唐老鸭,继承鸭子抽象类

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

    鸭子工厂类 DuckFactory

    1. public class DuckFactory {
    2. //私有化构造方法
    3. private DuckFactory(){}
    4. //static 饿汉模式实例出一个类型为鸭子工厂类的对象
    5. private static DuckFactory duckFactory = new DuckFactory();
    6. //定义图中的几种子类的鸭子类,类型用数字区分,可以手动增加
    7. public static final int WILD_DUCK = 1;
    8. public static final int RUBBER_DUCK = 2;
    9. public static final int DONALD_DUCK = 3;
    10. //依据鸭子类型得到鸭子实例的方法
    11. public static Duck getInstance(int duckType) {
    12. switch (duckType) {
    13. case WILD_DUCK:
    14. //返回直接实例化好的鸭子子类
    15. return new WildDuck();
    16. case RUBBER_DUCK:
    17. return new RubberDuck();
    18. case DONALD_DUCK:
    19. return new DonaldDuck();
    20. default:
    21. return null;
    22. }
    23. }
    24. }

    测试类

    1. public class Main {
    2. public static void main(String[] args) {
    3. //DuckFactory可以生产鸭子对象
    4. Duck donaldDuck = DuckFactory.getInstance(DuckFactory.DONALD_DUCK);
    5. donaldDuck.quack();
    6. Duck wildDuck = DuckFactory.getInstance(DuckFactory.WILD_DUCK);
    7. wildDuck.quack();
    8. Duck pjDuck = DuckFactory.getInstance(DuckFactory.PJ_DUCK);
    9. pjDuck.quack();
    10. }
    11. }

  • 相关阅读:
    ARM 37 个通用寄存器详解
    什么是SQL锁
    如何高效地从0搭建一个游戏项目
    MongoDB 中 查询(find) 指南
    四种主流的prompt框架
    第十章:异常
    LeetCode --- 2047. Number of Valid Words in a Sentence 解题报告
    09-单比特信号的跨时钟域处理
    Linux界的老古董
    R语言ggplot2添加单个文本或多个文本
  • 原文地址:https://blog.csdn.net/T278lk/article/details/125405596