常见的设计模式

2023-05-16

目录

    • 工厂模式
    • 单例模式
    • 观察者模式
    • 策略模式
    • 代理模式
    • 装饰模式

工厂模式

这个模式有三个对象:工厂、用户、产品;这么说可能还有点抽象,说具体点,就是当我们在代码中需要生成一个类实例时,不直接去拿到这个类名,然后new;而是通过一个工厂类(例如EntityFactory),给该工厂类传要创建的类名的字符串,获取到最后生成的类实例;这样做的好处是,将对象的构造和使用隔离,使之模块化,工厂化。


C#示例代码如下所示:

//设计模式----工厂模式示例代码
using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    abstract class Animal
    {
        public string name;
        public Animal(string _name) { name = _name; }

        abstract public void Func() ;
    }

    class Dog: Animal
    {
        public Dog(string _name) :base(_name) { name = _name; }
        override public void Func()
        {
            Console.WriteLine($"Dog, name is {name}");
        }
    }

    class Monkey:Animal
    {
        public Monkey(string _name) : base(_name) { name = _name; }
        override public void Func()
        {
            Console.WriteLine($"Monkey, name is {name}");
        }
    }

    class Duck:Animal
    {
        public Duck(string _name) : base(_name) { name = _name; }
        override public void Func()
        {
            Console.WriteLine($"Duck, name is {name}");
        }
    }

    class AnimalFactory // 只需要传字符串到工厂,就能生成对应的类实例,然后利用多态,调用一样的接口,对应不一样的实现
    {
        private static AnimalFactory Instance;
        public static AnimalFactory GetInstance()
        {
            if(Instance == null)
            {
                Instance = new AnimalFactory();
            }
            return Instance;
        }

      public dynamic CreateAnimal(string class_name, string animal_name)
        {
            Type type = Type.GetType("ConsoleApp1." + class_name); // 利用反射获取到对应类名的Type
            dynamic obj = Activator.CreateInstance(type, new object[] { animal_name}); //生成对应的实例
            return obj;
        }
    }

    class Program
    {
        
        public static void  Main()
        {
            Dictionary<string, string> nameDic = new Dictionary<string, string> 
            { 
                { "Dog", "xiaowang" },
                { "Duck", "xiaoli" },
                { "Monkey", "xiaozhang" },
            };

            foreach(var namePair in nameDic)
            {
                Animal animal = (Animal)AnimalFactory.GetInstance().CreateAnimal(namePair.Key, namePair.Value);
                animal?.Func();
            }
        } 
}
}

单例模式

在内存中只存在一个实例对象,通常的构成是:私有构造函数静态公共获取函数私有静态自身成员;创建单例的方式有懒汉模式饿汉模式静态内部类实现等。

这是C++实现

#include<iostream>
#include<string>
using namespace std;

class Singleton1 // 懒汉模式
{
private:
	static Singleton1* _instance;
	Singleton1() { cout << "Singleton1 created !!!!"; }
public:
	static Singleton1* GetInstance()
	{
		if (_instance == NULL)
		{
			_instance = new Singleton1();
		}
		return _instance;
	}
};
Singleton1* Singleton1::_instance; 
//--------------------------------------------------------------------------
class Singleton2 // 饿汉模式
{
private:
	static Singleton2* _instance;
	Singleton2() {cout<<"Singleton2 created !!!!"; };

public:
	static Singleton2* GetInstance()
	{
		return _instance;
	}
};

Singleton2* Singleton2::_instance = new Singleton2();

//------------------------------------------------------------------------------------

class Singleton3 // 局部静态变量实现,还有一种是加锁模式,比较麻烦且耗,需要的百度下就行
{
private:
	Singleton3() { cout << "Singleton3 created !!!!"; };
public:
	static Singleton3& GetInstance()
	{
		static Singleton3  instance;
		return instance;
	}
};

这是c#实现

// 设计模式————单例模式(懒汉、饿汉、静态内部类实现)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ConsoleApp1
{

    public class Singleton1 // 懒汉模式, 多线程不安全,会创建多个
    {
        private static Singleton1 _Singleton1 = null;
        private Singleton1()
        {
            Console.WriteLine("Singleton1被构造");
        }
        public static Singleton1 GetInstance()
        {
            if (_Singleton1 == null)
            {
                _Singleton1 = new Singleton1();
            }
            return _Singleton1;
        }
    }

    public class Singleton2  // 饿汉模式,这里多线程运行状态下是线程安全的
    {
        private Singleton2() { Console.WriteLine("Singleton2被构造"); }
        private static Singleton2 Instance = new Singleton2();
        public static Singleton2 GetInstance() 
        {
            return Instance;
        }
    }

    public class Singleton3 // 内部静态类实现,多线程安全
    {
        private Singleton3() { Console.WriteLine("Singleton3被构造"); }
        private static class InnerClass
        {
            internal static Singleton3 instance = new Singleton3();
            static InnerClass() {}  //静态构造函数
        }
        public static Singleton3 GetInstance()
        {
            return InnerClass.instance;
        }
    }

    public class Singleton4 //静态构造函数, 构造线程安全,保证内存只有一个这个实例对象
    {
        private static Singleton4 Instance;
        private Singleton4() { Console.WriteLine("Singleton4被构造"); }
        static Singleton4()
        {
            Instance = new Singleton4();
        }
        public static Singleton4 GetInstance()
        {
            return Instance;
        }
    }

    public class Singleton5 // 惰性构造,构造线程安全
    {
        private static readonly Singleton5 instance = new Singleton5();
        private Singleton5() { Console.WriteLine("Singleton5被构造"); }
        static Singleton5() {}  // 静态构造,保证只调用一次,并且在调用一个静态方法或者属性时,初始化这个类
        public static Singleton5 Instance
        {
            get
            {
                return instance;
            }
        }
    }

    class Program
    {
        public static void F1()
        {
            for (int i = 0; i < 10; ++i)
            {
                var a = Singleton2.GetInstance();
            }
        }
        public static void Main()
        {
            // 验证多线程状态下的线程安全
            Thread thread1 = new Thread(new ThreadStart(F1));
            thread1.Start();

            Thread thread2 = new Thread(new ThreadStart(F1));
            thread2.Start();
        }
    }
}

这是网上有另外一个大佬,关于c#单例的讲解

观察者模式

当一个事件发生时,需要去通知其他订阅者;例如游戏里面常见的领完奖励之后刷新UI界面等等。通过注册的方式,实现事件的统一下触发以及代码的隔离。

c#代码示例

//设计模式----观察者/订阅者模式
using System;

namespace ConsoleApp1
{
    public class Subscriber  // 发布者
    {
        public event Action mineActions;
        public Subscriber()
        {
        }

        public void registEvent(Action action)
        {

            mineActions += action;
        }

        public void unRegisterEvent(Action action)
        {
            mineActions -= action;
        }

        public void publishEvent()
        {
            mineActions?.Invoke();
        }
    }

    public abstract class ObserverBase
    {
        public string name;
        public ObserverBase(string _name)
        {
            name = _name;
        }
        public abstract void F1();

    }

    public class ObserverA: ObserverBase  // 订阅者A
    {
        public ObserverA(string _name):base(_name)
        {
            name = _name;
        }
        public override void F1()
        {
            Console.WriteLine($"this is {name} print !!!!!!!");
        }
    }

    public class ObserverB: ObserverBase // 订阅者B
    {
        public ObserverB(string _name) : base(_name)
        {
            name = _name;
        }
        public override void F1()
        {
            Console.WriteLine($"this is {name} print !!!!!!!");
        }
    }

    public class ObserverC : ObserverBase  // 订阅者C
    {
        public ObserverC(string _name) : base(_name)
        {
            name = _name;
        }
        public override void F1()
        {
            Console.WriteLine($"this is {name} print !!!!!!!");
        }
    }


    class Program
    {

        public static void Main()
        {
            Subscriber subscriber = new Subscriber();
            ObserverA observerA = new ObserverA("ObserverA");
            ObserverB observerB = new ObserverB("ObserverB");
            ObserverC observerC = new ObserverC("ObserverC");

            //订阅
            subscriber.registEvent(observerA.F1);
            subscriber.registEvent(observerB.F1);
            subscriber.registEvent(observerC.F1);

            //发布事件
            subscriber.publishEvent();

            //取消订阅
            subscriber.unRegisterEvent(observerA.F1);
            subscriber.unRegisterEvent(observerB.F1);
            subscriber.unRegisterEvent(observerC.F1);
        }
    }
}

策略模式

意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决: 在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。

同一种方式有多重实现方法,例如

  • 登录到某个网站,可以用手机号登录、扫码登录、账号密码登录等,都是为了登录到同一个账号。
  • 去沃尔玛买购物,结账的时候可以使用现金,支付宝、微信以及购物卡,都是为了结账。
  • 高德地图导航时,可以选择步行、骑车、开车等不同方式,给出的路线不一样。

代码示例

using System;
using System.Collections.Generic;
using System.Text;

namespace Strategy
{

    public abstract class StrategyBase
    {
        public abstract int F(int a, int b);
    }

    public class StrategyA: StrategyBase // 策略A
    {
        public override int F(int a, int b)
        {
            return a + b;
        }
    }

    public class StrategyB : StrategyBase // 策略B
    {
        public override int F(int a, int b)
        {
            return a - b;
        }
    }

    public class StrategyC : StrategyBase // 策略C
    {
        public override int F(int a, int b)
        {
            return a * b;
        }
    }

    public class Context // 代表上下文
    {
        StrategyBase strategyBase;
        public Context(StrategyBase _strategy)
        {
            strategyBase = _strategy;
        }

        public int F(int a, int b)
        {
            return strategyBase.F(a, b);
        }

    }

    public class Program
    {
        public static void Main()
        {
            Context a = new Context(new StrategyA()); // 使用策略A
            Context b = new Context(new StrategyB()); // 使用策略B
            Context c = new Context(new StrategyC()); // 使用策略C
            //调用的时候使用统一接口 F
            Console.WriteLine($"a.F(1,2) = {a.F(1, 2)}, b.F(1, 2) = {b.F(1, 2)}, c.F(1, 2) = {c.F(1, 2)}");
        }
    }
}

在上述的示例代码中,对于同样的使用环境(Context类),当使用不一样的策略是,可以有不一样的实现。


代理模式

在真实的某个对象上再包一层代码,可以很方便的扩展其功能;例如游戏中的相机,我们可以在原生相机的基础上包装一层,然后可以在这一层代理里面加一些保护、实现一些原生接口函数的组合功能等。

动机:限制对目标对象的直接访问,降低耦合度;还可以实现一些真实对象没有的接口或者一些现有功能的组合。

示例代码:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApp1
{
    class Subject // 真实对象
    {
        public void fun1()
        {

        }

        public void fun2()
        {

        }

        public void fun3()
        {

        }
    }

    class SubjectProxy  //代理对象,实现和真实对象一样的接口,可以限制对目标对象的直接访问,降低耦合度。
    {
        Subject subject;
        public SubjectProxy(Subject _subject)
        {
            subject = _subject;
        }

        public void fun1() // 这里做封装,可以打log,加保护
        {
            subject.fun1();
        }

        public void fun2()
        {
            subject.fun2();
        }

        public void fun3()
        {
            subject.fun3();
        }
    }

    public class Proxy
    {
        public static void Main()
        {
            SubjectProxy subjectProxy = new SubjectProxy(new Subject());
            subjectProxy.fun1();
            subjectProxy.fun2();
            subjectProxy.fun3();
        }
    }
}


装饰模式

可以对原来的函数或者类进行扩展;对于类的装饰,可以实现组件式的代码结构,使得各个功能可以和方便的添加与删除,并且代码的逻辑也会很清晰;实现的功能是以组件的形式实现与继承相似的效果。

解决的问题: 一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApp1
{

    public abstract class Shape // 抽象基类
    {
        public abstract void Print();

    }

    public class Circle: Shape // 圆形
    {
        public override void Print()
        {
            Console.WriteLine("This is Circle Draw !!");

        }
    }

    public class Triangle : Shape // 三角形
    {
        public override void Print()
        {
            Console.WriteLine("This is Triangle Draw !!");

        }
    }

    public class Decorator: Shape  // 装饰类
    {
        public Shape shape;

        public Decorator(Shape _shape)
        {
            shape = _shape;
        }
        public override void Print()
        {
            Console.WriteLine("This is Triangle Draw !!");

        }
    }

    public class ColorShapeDecorator : Decorator  // 装饰类型1
    {
        public ColorShapeDecorator(Shape _Shape) : base(_Shape)
        {

        }

        public override void Print()
        {
            shape.Print();
        }

        public void Color()  //相当于在传进来Shape的基础上,增加了Color接口
        {
            // DoSomething
            Console.WriteLine("This is Color function !!");
        }
    }

    public class GeometryShapeDecorator : Decorator  // 装饰类型2
    {
        public GeometryShapeDecorator(Shape _Shape) : base(_Shape)
        {

        }

        public override void Print()
        {
            shape.Print();
        }

        public void GetArea()  //相当于在传进来Shape的基础上,增加了GetArea接口
        {
            // DoSomething
            Console.WriteLine("this is GetArea function !!");
        }

        public void GetGirth()  //相当于在传进来Shape的基础上,增加了GetGirth接口
        {
            // DoSomething
            Console.WriteLine("this is GetGirth function !!");
        }
    }

    class DecoratorTest
    {
        public static void Main()
        {
            Circle circle = new Circle();
            circle.Print();
            Console.WriteLine("--------------------------------------------");
            Triangle triangle = new Triangle();
            triangle.Print();

            Console.WriteLine("--------------------------------------------");
            ColorShapeDecorator ShapeDecoratorShape1 = new ColorShapeDecorator(circle);
            ShapeDecoratorShape1.Print();
            ShapeDecoratorShape1.Color(); // 在circle的基础上扩展了Color函数

            Console.WriteLine("--------------------------------------------");
            GeometryShapeDecorator ShapeDecoratorShape2 = new GeometryShapeDecorator(triangle);
            ShapeDecoratorShape2.Print();
            ShapeDecoratorShape2.GetArea(); // 在triangle的基础上扩展了GetArea、GetGirth等函数
            Console.WriteLine("--------------------------------------------");
        }
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

常见的设计模式 的相关文章

  • cmake 学习使用笔记(二)库的生成与使用

    学习使用cmake 生成及使用库 xff0c 使用cmake 生成 lib 静态库 及动态库 xff08 dll xff09 及linux 下使用的静态库 a 和 动态库 xff08 so 也称为共享库 xff09 目录 使用工具 生成库
  • cmake 学习使用笔记(五)手动编译

    目录 编译cmake 项目 构建 方式一 xff1a 可执行文件生成的在当前目录 方式二 xff1a 可执行文件生成只制定目录 记录一下手动编译 cmake 项目 编译cmake 项目 项目结构 注意 xff1a 必须要有一个 CMakeL
  • 四旋翼无人机学习第5节--STM32最小系统外围电路分析

    文章目录 1 芯片手册分析2 stm32的外部晶振手册分析2 stm32的外部上电复位手册分析3 电源放置方法4 GND放置方法5 其他元器件放置方法6 网络放置方法7 快捷键分享8 原理图绘制成果分享 快速使用芯片的最好的方式 xff0c
  • 现代C++单文件库(header only)

    nlohmann json https github com nlohmann json easyloggingpp https github com amrayn easyloggingpp
  • 一、ROS创建工作空间

    一 ROS创建工作空间 1 什么是工作空间2 创建工作空间3 编译工作空间4 创建功能包5 编译功能包 xff08 重新编译工作空间 xff09 6 设置环境变量7 运行节点 1 什么是工作空间 工作空间 xff08 workspace x
  • 二、ROS集成开发环境

    二 ROS集成开发环境 1 安装terminator2 安装VScode3 在VScode中使用ROS3 1 创建工作空间3 2 启动VScode3 3 配置VScode3 4 创建功能包3 5 编写C 43 43 文件3 6 配置CMak
  • 十一、Ubuntu18.04下VSCode配置C/C++编译环境

    十一 Ubuntu18 04下VSCode配置C C 43 43 编译环境 1 安装VSCode2 安装插件3 创建工程4 总结 1 安装VSCode 这一步就不谈了 2 安装插件 编译一些简单的cpp文件 xff0c 下面几个插件就够用了
  • 十五、Typora官方主题 + 自定义主题

    十五 Typora官方主题 43 自定义主题 1 下载官方主题2 在官方主题的基础上自定义 1 下载官方主题 首先去Typora官网下载自己喜欢的主题 xff1a 官方主题下载 例如我这里选择 Vue 我们只用到里面的两个主题文件vue c
  • linux---进程控制

    进程控制 fork函数 创建一个子进程 pid t fork void 失败返回 1 xff1b 成功返回 xff1a 父进程返回子进程的ID 非负 子进程返回 0 pid t vfork void 同样时创建一个子进程 xff0c 但是他
  • 十六、windows11下VSCode配置C/C++编译环境

    十六 windows11下VSCode配置C C 43 43 编译环境 1 安装VSCode2 中文插件3 MinGW编译器下载和配置4 VSCode配置c c 43 43 编译环境5 测试是否配置成功6 使用万能头文件 include l
  • 十七、WSL2的安装与使用(Win11)

    十七 WSL2的安装与使用 xff08 Win11 xff09 1 准备工作1 1 开启主板 BIOS 虚拟化1 2 检查是否成功开启虚拟化1 3 开启开发者模式1 4 开启所需Windows功能1 5 下载 WSL 内核升级包 2 开始安
  • 十八、Ubuntu20.04 + VSCode + Opencv3.4.16 配置 + WSL2 可视化

    十八 Ubuntu20 04 43 VSCode 43 Opencv3 4 16 配置 43 WSL2 可视化 1 下载和解压 OpenCV3 4 162 使用 cmake 编译 OpenCV3 配置环境4 代码测试4 1 录制视频4 2
  • IntelRealSense 更新D435固件时,运行rs-fw-update -l后报错:找不到命令

    环境 xff1a Ubantu18 04 摄像头 xff1a IntelRealsense D435 首先确定电脑里已经成功安装了 librealsense 想要更新下D435的固件 xff0c 但是运行rs fw update l后报错
  • Linux _ Shell编程 — 变量

    一 shell脚本的基础知识 1 shell脚本的本质 语言的分类有 xff1a 编译性语言 C语言 C 43 43 语言 JAVA PHP Python等 xff0c 不同编译器编译的执行文件不同 xff0c 运行的平台也不尽相同 解释性
  • USART _ 两串口同时使用,冲突问题

    1 使用外设情况 xff1a 1 初始化了两个串口 xff1a 串口1以及串口2 xff0c 使用串口2DMA发送数据 xff0c 接收中断 接收数据 xff1b 使用串口1发送函数发送数据 xff0c 接收中断接收数据 xff1b 2 问
  • C语言 _ MakeFile(一)

    一 Make简介 工程管理器 xff0c 顾名思义 xff0c 是指管理较多的文件 Make工程管理器也就是个 自动编译管理器 xff0c 这里的 34 自动 34 是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量 xff0c
  • C语言_网络编程_SQLite3数据库项目 _ 在线词典

    一 项目分析 1 在线词典一般的饿运行过程 xff1a 例如 服务器端将用户信息和历史记录保存在数据库中 客户端输入用户和密码 xff0c 服务器端在数据库中查找 匹配 xff0c 返回结果 xff1b 2 项目的流程 定义数据库中表的结构
  • ARM体系结构与接口技术 —— ARM基础 及 指令

    一 ARM基础 1 ARM公司讲解 成立于1990年11月 前生为Acorn计算机公司 xff1b 主要设计ARM系列RISC处理器内核 xff1b 授权ARM内核给生产和销售半导体的合作伙伴 xff1b ARM公司不生产芯片 xff1b
  • 嵌入式基础_STM32F103C8T6从零开始建立一个项目(库函数)

    第一步 xff1a stm32f10x标准库下载 xff08 STSW STM32054 xff09 https www st com content st com zh products microcontrollers micropro
  • 自主实现一个minishell程序

    此时没有重定向功能 我们知道对于Linux xff0c shell就是个命令行解释器 xff0c 当我们输入相关的命令 xff0c 会去执行相关的操作 比如当我们输入ls a l命令 xff0c shell就会打印出当前目录的内容 xff0

随机推荐