软件设计模式与体系结构实验——2.3-1单列模式的应用

1. 实验目的

  1. 掌握单列模式的特点
  2. 分析具体问题,使用单列模式进行设计。

2. 实验内容

【作业2.3-1】在例2.6关于一个单位的互联网连接问题的软件设计中,单列模式部分采用的是标准单列模式,现在要求改变其设计,使用线程安全的单列模式。重新设计并且编写代码实现该互联网连接问题。具体要求以及代码实现参见光盘的相应作业部分。

3. 模式UML图

在这里插入图片描述
本题目设计程序的设计类图,及其他图:
在这里插入图片描述

4. 模式添加代码(JAVA语言实现)

(1)单列模式的标准实现方法

(1)President类

package com.glut.xusheng;

public class President {
    
    
    private static President instance;
    private String presidentName = null;
    //1.单列模式的标准实现方法
    private President(String name){
    
    
        presidentName = name;
    }
    public static President getInstance(String name){
    
    
        if (instance == null && !name.equals(null)){
    
    
            instance = new President(name);
        }
        return instance;
    }
    public void makeSpeech(){
    
    
        System.out.println("My name is" + presidentName);
        System.out.println("I am the president of the United States");
    }
}

(2)测试类TestSignleton

public class TestSignleton {
    
    
    public static void main(String[] args) {
    
    
        President president1 = President.getInstance("Bill Clinton");
        president1.makeSpeech();
        President president2 = President.getInstance("Bill Gates");
        president2.makeSpeech();
        President president3 = President.getInstance("Barack Obama");
        president3.makeSpeech();
    }
}

(3)类图
在这里插入图片描述

(4)运行截图
在这里插入图片描述

(2)使用线性安全单列模式设计

(1)President类

public class President {
    
    
    private static President instance;
    private String presidentName = null;
       //2.使用线性安全单列模式设计
    private President(String p){
    
    
        presidentName = p;
    }
    public static synchronized President getInstance(String p){
    
    
        if (instance == null && !p.equals(null)){
    
    
            instance = new President(p);
        }
        return instance;
    }
    public void makeSpeech(){
    
    
        System.out.println("My name is" + presidentName);
        System.out.println("I am the president of the US in the term 3001 - 3004");
    }
}

(2)运行截图
在这里插入图片描述

(3)双重同步锁(懒汉模式衍生版本)单列模式

(1)President类

public class President {
    
    
    private static President instance;
    private String presidentName = null;
        //3.单例模式---双重同步锁(懒汉模式衍生版本)
    private President(String name){
    
    
        presidentName = name;
    }
    public static President getInstance(String name){
    
    
    //这里前面如果不加volatile,那么后面的双重判断将会出现"误判",导致线程不安全,所以加了保证安全
    //第一重检查锁定
        if (instance == null) {
    
    
        synchronized (President.class) {
    
    
            //第二重检查锁定
            if (instance == null) {
    
    
                synchronized (President.class) {
    
    
                    //注意:非原子操作
                    instance = new President(name);
                }
            }
        }
    }
        return instance;
    }
    public void makeSpeech(){
    
    
        System.out.println("My name is" + presidentName);
        System.out.println("I am the president of the US 2022");
    }
}

(2)运行截图
在这里插入图片描述

(4)静态内部类单列模式

(1)President类

public class President {
    
    
      //4.单例模式---静态内部类(饿汉模式衍生版本)
    //优点:线程安全,并且实现了延时加载(调用效率高)
    //1:初始化对象(使用的静态内部类方式)
    //private static President instance;
    private String presidentName = null;
    private static class SingletonClassInstance {
    
    
        private static final President INSTANCE = new President(null);
    }

    //2:构造器私有化
    private President(String name){
    
    
        presidentName = name;
    }
    //3:提供获取单实例方法
    public static President getInstance(String name){
    
    
        return SingletonClassInstance.INSTANCE;
    }
    public void makeSpeech(){
    
    
        System.out.println("My name is" + presidentName);
        System.out.println("I am the president of the US 2233");
    }
}

(2)运行截图
在这里插入图片描述

(5)防止反射和反序列单列模式

(1)President类

 //6.单例模式---防止反射和反序列化漏
    public class President {
    
    

    //1:初始化对象
    //优点:这个是线程最安全,抗并发能力最强的
    // * 缺点:没有延时加载
    private static President instance;
    private String presidentName = null;
    //1:类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
        //2:私有化构造器
    private President(String name){
    
    
        if (instance != null){
    
    
            throw new RuntimeException();
        }
        presidentName = name;
    }
    //3:提供一个单实例方法,方法同步,调用效率低
    public static synchronized President getInstance(String name){
    
    
            if (instance == null ){
    
    
                instance = new President(name);
            }
            return instance;
        }
        public void makeSpeech(){
    
    
        System.out.println("My name is " + presidentName);
        System.out.println("I am the president of the United States2022");
    }
}

(2)运行截图
在这里插入图片描述

(6)枚举单列模式

(1)President类

//7.单例模式---枚举模式
    public class President {
    
    
        /**
         * 推荐使用
         * 优点:线程最安全
         */
        private static President singleton;
        //private static President instance;
        private String presidentName = null;
        //1.单列模式的标准实现方法
        private enum Singleton{
    
    
            INSTANCE;
            private President singleton;
            Singleton(){
    
    
                singleton = new President(name());
            }
            public President getInstance(){
    
    
                if (singleton == null ){
    
    
                    singleton = new President(getInstance().presidentName);
                }
                return singleton;
            }
        }
        private President(String name){
    
    
        presidentName = name;
    }
    public static President getInstance(String name){
    
    
        return Singleton.INSTANCE.singleton;
    }
    public void makeSpeech(){
    
    
        System.out.println("My name is " + presidentName);
        System.out.println("I am the president of the CHINA 2202");
    }

}

5. 整体代码

(1)ClientUI类

package com.glut.xusheng;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClientUI extends JFrame{
    
    
   private static final String CONNECT = "Create Connection";
   private static final String EXIT = "Exit";
   private JTextField txtInstruction;
   private SingleLogonGUI  objLogon = null;

   public ClientUI() {
    
    
      super("ClientUI - Singleton Pattern");
      JButton btnConnect = new JButton(CONNECT);
      JButton btnExit = new JButton(EXIT);
      btnConnect.setMnemonic(KeyEvent.VK_S);
      btnExit.setMnemonic(KeyEvent.VK_X);

      ButtonHandler vf = new ButtonHandler();
      btnConnect.addActionListener(vf);
      btnExit.addActionListener(vf);
      JPanel buttonPanel = new JPanel();
      buttonPanel.setBackground(Color.green);
      buttonPanel.add(btnConnect);
      buttonPanel.add(btnExit);

      txtInstruction = new JTextField("Click to get a connection");
      txtInstruction.setBackground(Color.green);
      Container contentPane = getContentPane();
      contentPane.setLayout(new BorderLayout());
      contentPane.add(buttonPanel, "South");
      contentPane.add(txtInstruction, "Center");
      setSize(320, 120);
      setVisible(true);
   }
   private void issueWarning(){
    
    
       txtInstruction.setText("Error. You cannot start a second connection.");
	}
   class ButtonHandler implements ActionListener {
    
    
      public void actionPerformed(ActionEvent e) {
    
    
         if (e.getActionCommand().equals(EXIT)) {
    
    
            System.exit(1);
         }
         else if (e.getActionCommand().equals(CONNECT)) {
    
    
			 if(objLogon == null)
			      objLogon = SingleLogonGUI.getInstance();
			 else
			 	  issueWarning();
      }
    }
  }
  public static void main(String[]  args) {
    
    
	     ClientUI connection = new  ClientUI();
  }
}// end of class

(2)President1类

package com.glut.xusheng;


/**
 * SingletonDemo1
 * 测试饿汉式单例模式 优点:调用效率高  缺点:不具备延时加载
 * @author
 * @version 1.0
 */
public class President1 {
    
    
    /**
     * 1 类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的.这里使用的private封装对象(主要是安全层面的访
     *   问修饰符,和一般的属性封装为private是一样的,没有什么特别的意思)
     *   static修饰是为了实例化对象的时候的方便使用类名.方法名的方式得到这个类的单例对象
     */
    private static President1 instance=new President1();

    /**
     * 2 私有构造器(在本类的如果要继续new对象可以,private,public修饰构造器没有区别,但是外部调用该类,必须使用private修饰以防止外部
     * new出新对象,所以这里必须是private修饰构造器)
     */
    private President1() {
    
    
    }
    /**
     * 3 公开调用方法,方法没有同步,调用效率高!(必须要提供给外部一个方法创建这个类的单例(唯一)对象的方法,因为第一步static修饰类属性的
     * (就是一个对象),所以这里也必须使用static修饰返回的对象)
     */
    public static President1 getInstance(){
    
    
        return instance;
    }

    /**
     * 这是在本类测试,所以上面的化构造器即使是private修饰为私有的,但是在本类仍然可以使用构造器new对象,所以我这里写的main方法主要是为了
     * 加深对构造器为什么要私有化的理解二特意在这里的,
     * 当然,要测试单例,应该在另一个类中写个main方法测试(或者junit测试)
     * @param args
     */

    public static void main(String[] args) {
    
    
        President1 instance = President1.getInstance();
        System.out.println(instance);
        President1 instance1 = President1.getInstance();
        System.out.println(instance1);
        /**
         * 构造器私有化了,为什么仍然可以new的原因是因为这个main方法写在了单例类的内部,当然可以使用构造器(故意写在这里的)
         */
        President1 singletonDemo1=new President1();
        President1 singletonDemo2=new President1();
        System.out.println(singletonDemo1);
        System.out.println(singletonDemo2);
        /**
         * 说明:instance和instance1肯定是一个对象,但是singletonDemo1和singletonDemo2不是一个对象了
         */
    }

}

(3)SingleLogonGUI类

package com.glut.xusheng;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/*==================================================*/
// This is a sigleton class, only one instance of the class can be created.
// To use this class, you cannot use constructor because it has been
// claimed private. You can only use the static method getInstance(),
// which, in case if the instance doesent exist, will create and return an
// instance of LogonGUI to the client class. otherwise, if an instance of
// LogonGUI has already been created before, this method will just
// return that instance to the client class.
/*==================================================*/
public class SingleLogonGUI extends JFrame {
    
    
   public static final String LOGON = "Log On";
   public static final String EXIT = "Exit";
   private String USERNM="mikesun";
   private String PASSWD = "opensesame";
   private JTextField txtUser;
   private JTextField txtPassWord;
   private static SingleLogonGUI instance = null;
   private JTextField txtInstruction;

   private SingleLogonGUI() {
    
    
	    super("SingleLogonGUI - Singleton");
	    initializeGUI();
    }
    public static SingleLogonGUI getInstance() {
    
    
	      if (instance == null)
		      instance = new SingleLogonGUI();
		  return instance;
    }
   public void initializeGUI()  {
    
    
      JLabel lblUsrName = new JLabel("User Name");
      JLabel lblPassWord = new JLabel("Password");
      txtUser = new JTextField(USERNM);
      txtPassWord = new JTextField(PASSWD);
      JButton btnLogon = new JButton(LOGON);
      btnLogon.setMnemonic(KeyEvent.VK_S);
      JButton btnExit = new JButton(EXIT);
      btnExit.setMnemonic(KeyEvent.VK_X);
      ButtonHandler vf = new ButtonHandler();
      btnLogon.addActionListener(vf);
      btnExit.addActionListener(vf);
      JPanel buttonPanel = new JPanel();
      GridBagLayout gridbag = new GridBagLayout();
      buttonPanel.setLayout(gridbag);
      GridBagConstraints gbc = new GridBagConstraints();

      buttonPanel.add(lblUsrName);
      buttonPanel.add(txtUser);
      buttonPanel.add(lblPassWord);
      buttonPanel.add(txtPassWord);
      buttonPanel.add(btnLogon);
      buttonPanel.add(btnExit);
      gbc.insets.top = 5;
      gbc.insets.bottom = 5;
      gbc.insets.left = 5;
      gbc.insets.right = 5;
      gbc.gridx = 0;
      gbc.gridy = 0;
      gridbag.setConstraints(lblUsrName, gbc);
      gbc.anchor = GridBagConstraints.WEST;
      gbc.gridx = 1;
      gbc.gridy = 0;
      gridbag.setConstraints(txtUser, gbc);
      gbc.gridx = 0;
	  gbc.gridy = 1;
	  gridbag.setConstraints(lblPassWord, gbc);
	  gbc.anchor = GridBagConstraints.WEST;
	  gbc.gridx = 1;
	  gbc.gridy = 1;
      gridbag.setConstraints(txtPassWord, gbc);
      gbc.anchor = GridBagConstraints.EAST;
      gbc.insets.left = 2;
      gbc.insets.right = 2;
      gbc.insets.top = 40;
      gbc.gridx = 0;
      gbc.gridy = 6;
      gridbag.setConstraints(btnLogon, gbc);
      gbc.anchor = GridBagConstraints.WEST;
      gbc.gridx = 1;
      gbc.gridy = 6;
      gridbag.setConstraints(btnExit, gbc);

      Container contentPane = getContentPane();
      contentPane.add(buttonPanel, BorderLayout.CENTER);
      txtInstruction = new JTextField();
      txtInstruction.setBackground(Color.pink);

      contentPane.add( txtInstruction, BorderLayout.NORTH);
      setSize(320, 200);
      setVisible(true);
   }
   public boolean isValideCustomer() {
    
    
	  String usr = txtUser.getText();
	  String pwd = txtPassWord.getText();
	  if(usr.equals(USERNM) && pwd.equals(PASSWD))
            return true;
      else
      	    return false;
   }
   private void issueWarning(){
    
    
         txtInstruction.setText("Invalide user name or password.");
	}
   private void issueSuccess(){
    
    
	     txtInstruction.setText("You have been successfully connected to the web.");
	}
   class ButtonHandler implements ActionListener  {
    
    
      public void actionPerformed(ActionEvent e) {
    
    
         if (e.getActionCommand().equals(EXIT)) {
    
    
            System.exit(1);
         }
         if (e.getActionCommand().equals(LOGON)) {
    
    
			 boolean isValideCus = isValideCustomer();
             if(isValideCus == false){
    
     //logon failed
                 issueWarning();
			 }
             else{
    
     //logon successfully
				 issueSuccess();
		     }
      }
    }
  }
}// end of class


(4)TestSignleton类

package com.glut.xusheng;

public class TestSignleton {
    
    
    public static void main(String[] args) {
    
    
        President president1 = President.getInstance("Bill Clinton");
        president1.makeSpeech();
        President president2 = President.getInstance("Bill Gates");
        president2.makeSpeech();
        President president3 = President.getInstance("Barack Obama");
        president3.makeSpeech();
    }
}

6. 运行截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7. 实验小结

单例模式概念以及优缺点:
(1)定义:
要求一个类只能生成一个对象,所有对象对它的依赖相同。
(2)优点:
只有一个实例,减少内存开支。应用在一个经常被访问的对象上减少系统的性能开销,应用启动时,直接产生一单例对象,用永久驻留内存的方式。
避免对资源的多重占用可在系统设置全局的访问点,优化和共享资源访问。
(3)缺点:
1.一般没有接口,扩展困难。原因:接口对单例模式没有任何意义;要求“自行实例化”,并提供单一实例,接口或抽象类不可能被实例化。(当然,单例模式可以实现接口、被继承,但需要根据系统开发环境判断)
2.单例模式对测试是不利的。如果单例模式没完成,是不能进行测试的。
3.单例模式与单一职责原则有冲突。原因:一个类应该只实现一个逻辑,而不关心它是否是单例,是不是要单例取决于环境;单例模式把“要单例”和业务逻辑融合在一个类。
(4)使用场景:
1.要求生成唯一序列化的环境

2.项目需要的一个共享访问点或共享的数据点
3.创建一个对象需要消耗资源过多的情况。如:要访问IO和 数据库等资源。
4.需要定义大量的静态常量和静态方法(如工具类)的环境。可以采用单例模式或者直接声明static的方式。
(5)注意事项:
1.类中其他方法,尽量是static
2.注意JVM的垃圾回收机制。
如果一个单例对象在内存长久不使用,JVM就认为对象是一个垃圾。所以如果针对一些状态值,如果回收的话,应用就会出现故障。
3.采用单例模式来记录状态值的类的两大方法:
(一)、由容器管理单例的生命周期。Java EE容器或者框架级容器,自行管理对象的生命周期。
(二)状态随时记录。异步记录的方式或者使用观察者模式,记录状态变化,确保重新初始化也可从资源环境获得销毁前的数据。

猜你喜欢

转载自blog.csdn.net/m0_52435951/article/details/125063646