IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 软件设计模式 -> 正文阅读

[PHP知识库]软件设计模式

模式

简介

软件设计模式,又称设计模式

  1. 是一套被反复使用、多数人知晓的,经过分类编目的,代码设计的经验总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案
  2. 使用设计模式是为了重用代码,让代码更容易被他人理解,保证代码的可靠性
  3. 合理运用设计模式可以完美的解决很多问题,每种模式都描述了在一个在我们中尉不断发生的问题,以及问题的核心解决方案

单例模式

1. 定义

??一个类只有一个实例,且该类能够自行创建这个实例的模式。
??确定某个类与且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象应该有且只有只一个
exp: Windows只能打开一个任务管理器

2. 结构

  1. 私有的静态的实例对象 private static instance
  2. 私有的构造函数 (保证在该类外部,无法通过new的方式来创建对象实例) private Singleton(){}
  3. 公有的、静态的、访问该实例对象的方法 public static Singleton getInstance(){}

3. 常见实现方式

1. 懒汉模式

public class Sington {
    //公有的访问单例实例的方法,当外部调用访问该实例的方法时,实例才被创建
    private static Sington instance = null;
    private Sington() {
        System.out.println("Only once!");
    }
    //私有的、静态的实例,设置为私有的防止外部直接访问该实例变量,设置为静态的,说明该实例是Sington类型的唯一的
    //若开始时,没有调用访问实例的方法,那么实例就不会自己创建
    private Sington(){}
    public static Sington getInstance(){
        //如果还没有被实例化过,就实例化一个,然后返回
        if(instance == null){
            instance = new Sington();
        }
        return instance;
    }
}

这段代码中未考虑线程安全,可能还会有参数多个实例,要对线程安全问题进行解决

public class Sington {
    private static Sington instance = null;
    private Sington(){}
    public static synchronized Sington getInstance(){
        //如果还没有被实例化过,就实例化一个,然后返回
        if(instance == null){
            instance = new Sington();
        }
        return instance;
    }
}

通过关键字synchronized确保线程getInstance安全。
弊端较大:

  1. 所有线程达到该方法后需要进行排队等候,对性能的损耗非常大

2. 饿汉模式

public class Sington {
 
    //私有化构造函数,防止在该类外部通过new的形式创建实例
    private Sington(){
        System.out.println("创建NoLazySingleton实例一次!");
    }
    //私有的、静态的实例,设置为私有的防止外部直接访问该实例变量,设置为静态的,说明该实例是Sington类型的唯一的
    //当系统加载Sington类文件的时候,就创建了该类的实例
    private static Sington instance = new Sington();
 
    //公有的访问单例实例的方法
    public static Sington getInstance(){
        return instance;
    }
}
饿汉模式懒汉模式
加载时已存在一个实例加载前无实例,加载后实例化,不一定需要使用
保证线程安全,采用JVM类加载的特性采用synchronized保障线程安全

JVM在加载类的时候采用单线程

3. 双重校检锁

public class Sington {
    private static Sington instance = null;
    private Sington(){}
    public static Sington getInstance(){
        if(instance == null){
            //保证线程安全
            synchronized (Singleton.class){
                //如果还没有被实例化过,就实例化一个,然后返回
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

说明:

  1. 双重检索也是一种延迟加载
  2. 较好的解决了确保线程安全的时候效率低下的问题

单例模式的实现原理:

  1. 一重校检: 也就是第一个if(singleton==null),这个是为了代码提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例
  2. 也就是第二个if(sington==null),这个校验是防止二次创建实例,假如有一种情况,当singleton还未被创建时,线程t1调用getInstance方法,由于第一次判断,此时线程t1准备继续执行,但是由于资源被线程t2抢占了,此时t2页调用getInstance方法,同样的,由于singleton并没有实例化,t2同样可以通过第一个if,然后继续往下执行,同步代码块,第二个if也通过,然后t2线程创建了一个实例singleton。此时t2线程完成任务,资源又回到t1线程,t1此时也进入同步代码块,如果没有这个第二个if,那么,t1就也会创建一个singleton实例,那么,就会出现创建多个实例的情况,但是加上第二个if,就可以完全避免这个多线程导致多次创建实例的问题

4. 静态内部类

public class Sington {
    private static class SingtonHolder{
        private static Sington instance = new Sington();
    }
    private Sington(){}
    public static Sington getInstance(){
        return SingtonHolder.instance;
    }
}

特性:

  1. 加载时不会加载内部静态类,使用时才会进行加载
  2. 使用线程加载又是安全的

5. 枚举

public enum Singleton {
    INSTANCE;
}

3. 特点

  1. 单例模式只有一个实例对象
  2. 该单例对象必须由单例类创建
  3. 单例类对外提供一个访问该单例的全局访问点

4. 优缺点

优点:

  1. 内存只存在一个实例,减少内存开销
  2. 避免对资源的多重占用
  3. 设置全局访问点,优化和共享资源访问

缺点:

  1. 一般没有接口,扩展困难
  2. 不利于代码调试

工厂模式

1. 定义

  1. 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  2. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

2. 常见分类模式

1. 简单工厂模式

“产品”: 被创建的对象
“工厂”: 创建产品的对象
如果创建的产品不多,只要一个工厂类就可以完成,称为“简单工厂模式”,也叫做“静态工厂方法模式”
组成:

  1. 简单工厂:核心,负责创建所有实例的内部逻辑
  2. 抽象产品:创建所有对象的父类,负责描述所有实例共有的公共接口
  3. 具体产品:创建目标

在这里插入图片描述

public class Client {
    //抽象产品
    public interface Product {
        void show();
    }
    //具体产品:ProductA
    static class ConcreteProduct1 implements Product {
        public void show() {
            System.out.println("具体产品1显示...");
        }
    }
    //具体产品:ProductB
    static class ConcreteProduct2 implements Product {
        public void show() {
            System.out.println("具体产品2显示...");
        }
    }
    final class Const {
        static final int PRODUCT_A = 0;
        static final int PRODUCT_B = 1;
        static final int PRODUCT_C = 2;
    }
    static class SimpleFactory {
        public static Product makeProduct(int kind) {
            switch (kind) {
                case Const.PRODUCT_A:
                    return new ConcreteProduct1();
                case Const.PRODUCT_B:
                    return new ConcreteProduct2();
            }
            return null;
        }
    }
}

优点:

  1. 包含必要的逻辑判断,可以决定在什么时间创建哪一个产品的实例
  2. 客户只需要知道参数
  3. 可引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类

缺点:

  1. 工厂类单一,一旦异常,整个系统受影响
  2. 代码臃肿,违背高聚合原则
  3. 增加系统中类的个数,增加系统的复杂度和理解难度
  4. 扩展困难,增加新产品需要修改工厂逻辑
  5. 工厂角色不能形成基于继承的等级结构

2. 工厂方法模式

“工厂方法模式”是对简单工厂模式进一步抽象,可以在不修改原系统代码的情况下引进新产品,满足开闭原则
组成:

  1. 抽象工厂:提供创建产品的接口,通过抽象工厂访问具体工厂创建产品
  2. 具体工厂:实现抽象工厂中的抽象方法,完成创建
  3. 抽象产品:产品规范
  4. 具体产品:实现抽象产品角色定义的接口,与具体工厂一一对应
    在这里插入图片描述
public class AnimalFarmTest {
    public static void main(String[] args) {
        try {
            Animal a;
            AnimalFarm af;
            af = (AnimalFarm) ReadXML2.getObject();
            //抽象工厂内容放置到配置文件中
            a = af.newAnimal();
            a.show();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
//抽象产品:动物类,提供了产品的接口
interface Animal {
    public void show();
}
//具体产品:马类,实现抽象产品中的抽象方法
class Horse implements Animal {
    JScrollPane sp;
    JFrame jf = new JFrame("工厂方法模式测试");
    public Horse() {
        Container contentPane = jf.getContentPane();
        JPanel p1 = new JPanel();
        p1.setLayout(new GridLayout(1, 1));
        p1.setBorder(BorderFactory.createTitledBorder("动物:马"));
        sp = new JScrollPane(p1);
        contentPane.add(sp, BorderLayout.CENTER);
        JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg"));
        p1.add(l1);
        jf.pack();
        jf.setVisible(false);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    //用户点击窗口关闭
    }
    public void show() {
        jf.setVisible(true);
    }
}
//具体产品:牛类
class Cattle implements Animal {
    JScrollPane sp;
    JFrame jf = new JFrame("工厂方法模式测试");
    public Cattle() {
        Container contentPane = jf.getContentPane();
        JPanel p1 = new JPanel();
        p1.setLayout(new GridLayout(1, 1));
        p1.setBorder(BorderFactory.createTitledBorder("动物:牛"));
        sp = new JScrollPane(p1);
        contentPane.add(sp, BorderLayout.CENTER);
        JLabel l1 = new JLabel(new ImageIcon("src/A_Cattle.jpg"));
        p1.add(l1);
        jf.pack();
        jf.setVisible(false);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    //用户点击窗口关闭
    }
    public void show() {
        jf.setVisible(true);
    }
}
//抽象工厂:畜牧场
interface AnimalFarm {
    public Animal newAnimal();
}
//具体工厂:养马场
class HorseFarm implements AnimalFarm {
    public Animal newAnimal() {
        System.out.println("新马出生!");
        return new Horse();
    }
}
//具体工厂:养牛场
class CattleFarm implements AnimalFarm {
    public Animal newAnimal() {
        System.out.println("新牛出生!");
        return new Cattle();
    }
}
package FactoryMethod;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
class ReadXML2 {
    //该方法用于从xml配置文件中提取具体的类名,并返回一个实例对象
    public static Object getObject() {
        try {
            //创建一个文档对象
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            doc = builder.parse(new File("src/FactoryMethod/config2.xml"));
            //获取包含类名的文本节点
            NodeList nl = doc.getElementsByTagName("className");
            Node classNode = nl.item(0).getFirstChild();
            String cName = "FactoryMethod." + classNode.getNodeValue();
            //通过类名生成实例对象并返回
            Class<?> c = Class.forName(cName);
            Object obj = c.newInstance();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

优点:

  1. 用户通过具体工厂名称就可以得到所要的产品,无需知道创建过程
  2. 灵活性强,
  3. 解耦框架,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点:

  1. 类过多,增加复杂度

3. 抽象工厂模式

1. 定义

??是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂可以生产多个等级的产品

模式:

  1. 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  2. 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

组成:

  1. 抽象工厂
  2. 具体工厂
  3. 抽象产品
  4. 具体产品
public class FarmTest {
    public static void main(String[] args) {
        try {
            Farm f;
            Animal a;
            Plant p;
            //读取响应的配置信息
            f = (Farm) ReadXML.getObject();
            a = f.newAnimal();
            p = f.newPlant();
            a.show();
            p.show();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
//抽象产品:植物类
interface Plant {
    public void show();
}
//具体产品:水果类
class Fruitage implements Plant {

    public Fruitage() {
        System.out.println("具体水果类生成");
    }
    public void show() {
        System.out.println("执行水果类的相应操作");
    }
}
//具体产品:蔬菜类
class Vegetables implements Plant {
    public Vegetables() {
        System.out.println("具体蔬菜类生成");
    }
    public void show() {
        System.out.println("执行蔬菜类的相应操作");
    }
}
//抽象产品:动物类
interface Animal {
    public void show();
}
//具体产品:马类
class Horse implements Animal {
    JScrollPane sp;
    JFrame jf = new JFrame("抽象工厂模式测试");
    public Horse() {
        Container contentPane = jf.getContentPane();
        JPanel p1 = new JPanel();
        p1.setLayout(new GridLayout(1, 1));
        p1.setBorder(BorderFactory.createTitledBorder("动物:马"));
        sp = new JScrollPane(p1);
        contentPane.add(sp, BorderLayout.CENTER);
        JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg"));
        p1.add(l1);
        jf.pack();
        jf.setVisible(false);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用户点击窗口关闭
    }
    public void show() {
        jf.setVisible(true);
    }
}
//抽象工厂:农场类
interface Farm {
    public Animal newAnimal();
    public Plant newPlant();
}
//具体工厂:农场类1
class SGfarm implements Farm {
    public Animal newAnimal() {
        System.out.println("新牛出生!");
        return new Cattle();
    }
    public Plant newPlant() {
        System.out.println("蔬菜长成!");
        return new Vegetables();
    }
}

优点:

  1. 类内部对产品组相关联多等级产品共同管理
  2. 需要产品族,抽象工厂使用同一产品组
    缺点
  3. 增强拓展性

总结

  1. 使用设计模式的目的是为了提高代码的可重用性、代码的可读性和代码的可靠性
  2. 懒汉模式将实例化时机存放到需要使用的时候,“延迟加载”,避免在加载中实例化用不到的实例,但是需要解决线程安全问题
  3. 双重校检锁是一种延迟加载,并且可以较好的解决在确保线程安全的前提下效率低下的问题,锁住部分代码。
  4. 对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数
  5. 抽象工厂模式当增加一个产品族满足开闭原则,添加一个新种类的产品,不满开闭原则

参考

设计模式——单例模式
JAVA设计模式系列——工厂模式
设计模式系列——抽象工厂模式
深入理解单例模式(附简单案例)
抽象工厂模式(详解版)

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-09-18 09:51:10  更:2021-09-18 09:52:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 19:24:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码