0%

在使用docker的时候,难免会遇到当前主机不支持运行docker的情况,这时可以使用运程ssh的形式连接一个已经创建了docker的主机,本地正常使用docker命令

Docker Machine Generic驱动

首先说明一下 Docker Machine Generic驱动
它可以 创建一个machines通过SSH使用已经存在的虚拟机或是主机。

Example
创建一个machine的实例,需要指定–driver generic,主机的IP地址,DNS名和SSH私钥路径去连接它.

1
2
3
4
5
docker-machine create \
--driver generic \
--generic-ip-address=203.0.113.81 \
--generic-ssh-key ~/.ssh/id_rsa \
vm

除了上述的命令,你还可以使用的选项如下

1
2
3
4
5
6
Options
--generic-engine-port:Docker Daemon使用的端口(注意:这个标识在boot2docker中无效)
--generic-ip-address:必需字段,主机IP地址.
--generic-ssh-key:SSH user的私钥路径.
--generic-ssh-user:SSH连接使用的username.
--generic-ssh-port:SSH使用的端口.

之后 eval $(docker-machine env vm)
注意 这里 vm 是你设置的 docker machine 名字

执行一个 hello world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

Docker的安装是非常方便的,但是有一些细节还是要注意一下,下面打开博客了解更多内容吧。

阅读全文 »

分布式事务的背景

当系统架构使用非单体应用的时候,如采用 面向服务架构、徽服务架构的时候,会将项目拆分为不同的应用服务到单独的运行环境中,应用服务之间通讯使用的如wcf、http、rpc等等远程调用技术、每个服务中都有自己的数据库数据源和本地事务,服务之间互不影响,这种情况下就产生了分布式事务的问题。

CAP是什么

CAP定律是指在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

注意

1、这里说的是在一个分布式系统中才会出现的问题,在单体应用中不会出现CAP三者不可兼得的问题。

2、关于权衡,不要以为在所有时候都只能选择两个特性。在不存在网络失败的情况下(分布式系统正常运行时的必要条件),C和A能够同时保证。只有当网络失败时,才会在C和A之间做出选择。

接下来我们具体来了解CAP具体是什么?

Consistency(一致性)

对于客户端的每次读操作,要么读到的是最新的数据,要么读取失败。换句话说,一致性是站在分布式系统的角度,对访问本系统的客户端的一种承诺:要么我给您返回一个错误,要么我给你返回绝对一致的最新数据,不难看出,其强调的是数据正确。

Availability(可用性)

任何客户端的请求都能得到响应数据,不会出现响应错误。换句话说,可用性是站在分布式系统的角度,对访问本系统的客户的另一种承诺:我一定会给您返回数据,不会给你返回错误,但不保证数据最新,强调的是不出错。

Partition tolerance(分区容错性)

由于分布式系统通过网络进行通信,网络是不可靠的。当任意数量的消息丢失或延迟到达时,系统仍会继续提供服务,不会挂掉。换句话说,分区容忍性是站在分布式系统的角度,对访问本系统的客户端的再一种承诺:我会一直运行,不管我的内部出现何种数据同步问题,强调的是不挂掉。

CAP的取舍权衡

C、A、P三个特性只能满足其中两个,那么取舍的策略共有3种:

  • CA没有P;如果没有P就是没有分区的意思,这咱情况下系统没有扩展性,无法部署节点、也就无法完成分布式设计的思想。
  • CP没有A;不要可用性的情况下,每个服务器之间保持强一致性而P(分区)之间的数据同步需要一定的时间,同步期间用户会有不好的体验,有很多系统使用的是CP的方案,如 Redis、HBase等
  • AP没有C;如果没有C一致性,系统可以高可用分区的情况下,P分区情况下节点之间,节点使用本地数据提供服务,这样的方案下数据在全局来看无法达到一致性。一般在抢购的场景下会出现这种情况,如看到库存还在,但是抢购下单的时候发现库存已经不在了。牺牲数据一致的用户体验,让系统整体可以运行,不会阻塞用户的购物流程。

一般的大型互联网应用采用多主机,集群化部署的方式。这种多节点的架构方式下,对于P的容错是分布式系统架构下一定要面对的问题,于是只能在C和A之间做取舍,但是有时对于一些对C强一致性要求极高的场景中,如银行转账可以在A和P之间取舍,如果出现网络故障,宁可停止服务。

具体可以根据实际的业务场景选择适合的架构设计。

参考:

分布式CAP定理,为什么不能同时满足三个特性?_cpa原则三个特性-CSDN博客

设计模式是软件开发中常用的解决方案,而设计模式的六大原则则是指导我们创建可维护、可扩展、可重用的高质量软件的基本准则。在本文中,我们将深入探讨这六大原则,并为每个原则提供具体的例子。

1. 单一职责原则(Single Responsibility Principle - SRP)

单一职责原则要求一个类应该只有一个引起变化的原因。这意味着一个类应该只负责一项职责。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
public class FileManager
{
public void SaveFile(string content)
{
// 保存文件的具体实现
}

public void ParseFile(string filePath)
{
// 解析文件的具体实现
}
}

在上述例子中,FileManager类分别负责保存文件和解析文件,遵循了单一职责原则。

2. 开放封闭原则(Open-Closed Principle - OCP)

开放封闭原则要求软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。即通过添加新的代码来扩展现有功能,而不是修改已有代码。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface IShape
{
void Draw();
}

public class Circle : IShape
{
public void Draw()
{
// 画圆的具体实现
}
}

public class Square : IShape
{
public void Draw()
{
// 画正方形的具体实现
}
}

在上述例子中,通过添加新的实现类(如Triangle),我们可以轻松扩展支持新的形状,而不需要修改IShape接口及其现有实现类。

3. 里氏替换原则(Liskov Substitution Principle - LSP)

里氏替换原则要求子类能够替换其父类而不影响程序的正确性。即如果一个类型是父类型,那么它应该能够被子类型替代。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Rectangle
{
protected int Width;
protected int Height;

public void SetWidth(int width)
{
Width = width;
}

public void SetHeight(int height)
{
Height = height;
}
}

public class Square : Rectangle
{
public override void SetWidth(int width)
{
Width = width;
Height = width;
}

public override void SetHeight(int height)
{
Width = height;
Height = height;
}
}

在上述例子中,Square类继承自Rectangle类,并重写了SetWidthSetHeight方法,保证了子类能够替换父类而不破坏程序的正确性。

4. 依赖倒置原则(Dependency Inversion Principle - DIP)

依赖倒置原则要求高层模块不应该依赖于低层模块,而是应该依赖于抽象。同时,抽象不应该依赖于细节,细节应该依赖于抽象。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public interface IWriter
{
void Write(string message);
}

public class ConsoleWriter : IWriter
{
public void Write(string message)
{
Console.WriteLine("Writing to console: " + message);
}
}

public class MessageProcessor
{
private readonly IWriter _writer;

public MessageProcessor(IWriter writer)
{
_writer = writer;
}

public void ProcessMessage(string message)
{
// 处理消息的逻辑
_writer.Write(message);
}
}

在上述例子中,MessageProcessor高层模块依赖于抽象IWriter接口,而不直接依赖于具体的ConsoleWriter实现,符合依赖倒置原则。

5. 接口隔离原则(Interface Segregation Principle - ISP)

接口隔离原则要求一个类不应该强迫其它类实现它们用不到的方法。接口应该小而专一。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public interface IWorker
{
void Work();
void Eat();
}

public class Programmer : IWorker
{
public void Work()
{
// 程序员的工作
}

public void Eat()
{
// 程序员的吃饭
}
}

public class Janitor : IWorker
{
public void Work()
{
// 看门员的工作
}

public void Eat()
{
// 看门员的吃饭
}
}

在上述例子中,通过接口隔离原则,ProgrammerJanitor只需实现与自己相关的方法,避免了强迫实现不需要的方法。

6. 合成复用原则(Composite Reuse Principle - CRP)

合成复用原则要求尽量使用对象组合,而不是继承。通过组合现有的对象来实现新的功能,而不是通过继承现有的类。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Engine
{
public void Start()
{
// 引擎启动逻辑
}
}

public class Car
{
private readonly Engine _engine;

public Car(Engine engine)
{
_engine = engine;
}

public void Start()
{
_engine.Start();
// 其他汽车启动逻辑
}
}

在上述例子中,Car类通过组合Engine类来实现汽车的启动功能,而不是通过继承Engine类。

通过理解和遵循这六

大原则,开发人员可以更好地设计出灵活、可维护的软件系统。这些原则为面向对象设计提供了指导方针,有助于构建更加健壮和可扩展的应用程序。

状态模式 - 行为型模式的状态切换之旅

在软件设计中,状态模式是一种行为型设计模式,它允许一个对象在其内部状态改变时改变其行为。状态模式的核心思想是将对象的行为与其状态分离,使得在不同的状态下可以选择不同的行为。本文将深入讨论状态模式的概念、实现方式以及在实际应用中的使用场景。

状态模式的概念

状态模式(State Pattern)是一种行为型设计模式,其核心思想是允许一个对象在其内部状态改变时改变其行为。状态模式将对象的状态和行为分离,使得对象在不同的状态下可以选择不同的行为。状态模式主要包括三个角色:上下文(Context)、抽象状态(State)和具体状态(ConcreteState)。

状态模式的 UML 类图

classDiagram
    class Context {
        - state: State
        + Request(): void
        + SetState(state: State): void
    }

    class State {
        + Handle(context: Context): void
    }

    class ConcreteStateA
    class ConcreteStateB

    Context --> State
    State <|.. ConcreteStateA
    State <|.. ConcreteStateB

状态模式的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System;

// 抽象状态类
public abstract class State
{
public abstract void Handle(Context context);
}

// 具体状态类 A
public class ConcreteStateA : State
{
public override void Handle(Context context)
{
Console.WriteLine("Handling with ConcreteStateA");
// 在具体状态类中可以改变上下文的状态
context.SetState(new ConcreteStateB());
}
}

// 具体状态类 B
public class ConcreteStateB : State
{
public override void Handle(Context context)
{
Console.WriteLine("Handling with ConcreteStateB");
// 在具体状态类中可以改变上下文的状态
context.SetState(new ConcreteStateA());
}
}

// 上下文类
public class Context
{
private State state;

public Context(State initialState)
{
this.state = initialState;
}

public void Request()
{
// 上下文类将请求委托给当前状态处理
state.Handle(this);
}

public void SetState(State newState)
{
this.state = newState;
}
}

状态模式的应用场景

状态模式适用于以下情况:

  1. 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
  2. 一个操作中含有大量的条件语句,并且这些条件语句的目的是根据对象的状态选择不同的行为。
  3. 一个对象需要根据内部状态来改变它的状态。

状态模式的优势

  1. 封装性好: 状态模式将一个对象的状态封装到不同的状态类中,使得每个状态类的实现都相对独立,便于维护和扩展。
  2. 可扩展性: 可以轻松地增加新的状态类,扩展系统的功能。
  3. 避免条件语句: 状态模式通过将不同的状态分离,避免了大量的条件语句,使得代码更加清晰。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Program
{
static void Main()
{
// 创建上下文对象,初始状态为 ConcreteStateA
Context context = new Context(new ConcreteStateA());

// 发起请求,由 ConcreteStateA 处理
context.Request();

// 发起请求,由 ConcreteStateB 处理
context.Request();
}
}

总结

状态模式是一种行为型设计模式,通过将对象的状态和行为分离,使得对象在不同的状态下可以选择不同的行为。状态模式适用于一个对象的行为取决于其内部状态,并且需要在运行时根据状态改变行为的场景。在实际应用中,状态模式常用于处理有限状态机、状态切换等场景。

中介者模式 - 行为型模式的协调调停者

在软件设计中,中介者模式是一种行为型设计模式,它定义了一个中介者对象,该对象封装了一组对象之间的交互方式。中介者模式使得对象之间不直接相互通信,而是通过中介者对象进行协调,降低了对象之间的耦合性。本文将深入讨论中介者模式的概念、实现方式以及在实际应用中的使用场景。

中介者模式的概念

中介者模式(Mediator Pattern)是一种行为型设计模式,其核心思想是定义一个中介者对象,该对象封装了一组对象之间的交互方式。中介者模式使得对象之间不直接相互通信,而是通过中介者对象进行协调。这样的设计降低了对象之间的耦合性,使得系统更加灵活和易于维护。

中介者模式的 UML 类图

classDiagram
    class Mediator {
        + RegisterColleague(colleague: Colleague): void
        + SendMessage(colleague: Colleague, message: string): void
    }

    class Colleague {
        - mediator: Mediator
        + Send(message: string): void
        + Receive(message: string): void
    }

    class ConcreteMediator {
        - colleagues: List
        + RegisterColleague(colleague: Colleague): void
        + SendMessage(colleague: Colleague, message: string): void
    }

    class ConcreteColleagueA
    class ConcreteColleagueB

    Mediator <|.. ConcreteMediator
    Colleague <|.. ConcreteColleagueA
    Colleague <|.. ConcreteColleagueB

中介者模式的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using System;
using System.Collections.Generic;

// 抽象中介者
public interface Mediator
{
void RegisterColleague(Colleague colleague);
void SendMessage(Colleague colleague, string message);
}

// 抽象同事类
public abstract class Colleague
{
protected Mediator mediator;

public Colleague(Mediator mediator)
{
this.mediator = mediator;
}

public abstract void Send(string message);
public abstract void Receive(string message);
}

// 具体中介者
public class ConcreteMediator : Mediator
{
private List<Colleague> colleagues;

public ConcreteMediator()
{
this.colleagues = new List<Colleague>();
}

public void RegisterColleague(Colleague colleague)
{
colleagues.Add(colleague);
}

public void SendMessage(Colleague colleague, string message)
{
foreach (var c in colleagues)
{
if (c != colleague)
{
c.Receive(message);
}
}
}
}

// 具体同事类A
public class ConcreteColleagueA : Colleague
{
public ConcreteColleagueA(Mediator mediator) : base(mediator) { }

public override void Send(string message)
{
Console.WriteLine("Colleague A sends message: " + message);
mediator.SendMessage(this, message);
}

public override void Receive(string message)
{
Console.WriteLine("Colleague A receives message: " + message);
}
}

// 具体同事类B
public class ConcreteColleagueB : Colleague
{
public ConcreteColleagueB(Mediator mediator) : base(mediator) { }

public override void Send(string message)
{
Console.WriteLine("Colleague B sends message: " + message);
mediator.SendMessage(this, message);
}

public override void Receive(string message)
{
Console.WriteLine("Colleague B receives message: " + message);
}
}

中介者模式的应用场景

中介者模式适用于以下情况:

  1. 一组对象之间存在复杂的交互关系,导致它们之间的通信结构复杂难以理解。
  2. 对象之间的交互行为需要随时变化,或者需要增加新的对象时。
  3. 系统中的对象由于通信关系过于紧密,导致它们之间的耦合性较高。

中介者模式的优势

  1. 降低耦合性: 中介者模式通过将对象之间的通信集中在中介者中,降低了对象之间的耦合性。
  2. 简化对象交互: 中介者模式将对象之间的交互方式封装在中介者中,使得对象之间的通信更加简单清晰。
  3. 易于扩展: 可以通过增加新的同事类和调整中介者的实现,轻松地扩展系统。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
static void Main()
{
// 创建具体中介者对象
ConcreteMediator mediator = new ConcreteMediator();

// 创建具体同事类对象,并注册到中介者
ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
mediator.RegisterColleague(colleagueA);
mediator.RegisterColleague(colleagueB);

// 同事类之间通过中介者进行通信
colleagueA.Send("Hello from Colleague A");
colleagueB.Send("Hi from Colleague B");
}
}

总结

中介者模式是一种行为型设计模式,通过定义一个中介者对象,封装了一组对象之间的交互方式,降低了对象之间的耦合性。中介者模式使得对象之间不直接相互通信,而是通过中介者对象进行协调。在实际应用中

职责链模式 - 行为型模式的责任传递者

在软件设计中,职责链模式是一种行为型设计模式,它通过一系列处理对象组成的链条,依次处理请求,直到找到合适的处理者。职责链模式可以有效地解耦发送者和接收者,并允许多个对象处理同一个请求。本文将深入讨论职责链模式的概念、实现方式以及在实际应用中的使用场景。

职责链模式的概念

职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式,其核心思想是将处理对象组成一条链,依次处理请求,直到找到合适的处理者。每个处理对象都包含一个指向下一个处理对象的引用,形成一个处理链。请求从链的头部开始,沿着链条传递,直到有一个处理对象能够处理该请求。

职责链模式的 UML 类图

classDiagram
    class Handler {
        - successor: Handler
        + SetSuccessor(successor: Handler): void
        + HandleRequest(request: int): void
    }

    class ConcreteHandlerA {
        + HandleRequest(request: int): void
    }

    class ConcreteHandlerB {
        + HandleRequest(request: int): void
    }

    Handler <|-- ConcreteHandlerA
    Handler <|-- ConcreteHandlerB

职责链模式的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;

// 抽象处理者类
public abstract class Handler
{
protected Handler successor;

public void SetSuccessor(Handler successor)
{
this.successor = successor;
}

public abstract void HandleRequest(int request);
}

// 具体处理者A
public class ConcreteHandlerA : Handler
{
public override void HandleRequest(int request)
{
if (request >= 0 && request < 10)
{
Console.WriteLine($"Concrete Handler A handles request {request}");
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}

// 具体处理者B
public class ConcreteHandlerB : Handler
{
public override void HandleRequest(int request)
{
if (request >= 10 && request < 20)
{
Console.WriteLine($"Concrete Handler B handles request {request}");
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}

职责链模式的应用场景

职责链模式适用于以下情况:

  1. 多个对象可以处理同一个请求,但具体由哪个对象处理在运行时确定。
  2. 系统需要动态地指定处理一个请求的对象集合。
  3. 请求的发送者和接收者之间需要解耦,使得系统更灵活。

职责链模式的优势

  1. 解耦发送者和接收者: 职责链模式可以有效地解耦请求的发送者和接收者,每个处理者只关心自己的处理逻辑。
  2. 灵活性和可扩展性: 可以动态地调整链上处理对象的顺序或者增加新的处理对象,提高系统的灵活性和可扩展性。
  3. 单一职责原则: 每个具体处理者都只负责自己能够处理的请求,符合单一职责原则。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Program
{
static void Main()
{
// 创建具体处理者对象
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();

// 设置处理链
handlerA.SetSuccessor(handlerB);

// 发送请求,实际上会从处理链的头部开始,依次尝试每个处理者
handlerA.HandleRequest(15);
}
}

总结

职责链模式是一种行为型设计模式,通过将处理对象组成一条链,依次处理请求,实现了请求的发送者和接收者的解耦。职责链模式在需要多个对象处理同一个请求,且具体处理对象在运行时确定的情景中有广泛的应用。在实际应用中,职责链模式需要注意设计链上处理对象的逻辑,以确保请求能够正确地被处理。

模板方法模式 - 行为型模式的骨架定义者

在软件设计中,模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,将算法中的一些步骤延迟到子类中实现。模板方法模式允许子类在不改变算法结构的情况下重新定义算法中的某些步骤。本文将深入讨论模板方法模式的概念、实现方式以及在实际应用中的使用场景。

模板方法模式的概念

模板方法模式(Template Method Pattern)是一种行为型设计模式,其核心思想是定义一个算法的骨架,将算法中的一些步骤延迟到子类中实现。模板方法模式在父类中定义算法的结构,而将具体步骤的实现交给子类。这样,子类可以在不改变算法结构的情况下重新定义算法的某些步骤。

模板方法模式的 UML 类图

classDiagram
    class AbstractClass {
        # PrimitiveOperation1(): void
        # PrimitiveOperation2(): void
        + TemplateMethod(): void
    }

    class ConcreteClass {
        + PrimitiveOperation1(): void
        + PrimitiveOperation2(): void
    }

    AbstractClass <|-- ConcreteClass

模板方法模式的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;

// 抽象类
public abstract class AbstractClass
{
// 模板方法定义了算法的骨架,将某些步骤的实现交给子类
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
}

// 原语操作1,延迟到子类实现
protected abstract void PrimitiveOperation1();

// 原语操作2,延迟到子类实现
protected abstract void PrimitiveOperation2();
}

// 具体类
public class ConcreteClass : AbstractClass
{
// 实现原语操作1
protected override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClass Primitive Operation 1");
}

// 实现原语操作2
protected override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClass Primitive Operation 2");
}
}

模板方法模式的应用场景

模板方法模式适用于以下情况:

  1. 多个子类有共同的算法骨架,但其中某些步骤的实现可能不同。
  2. 不想让子类改变算法的骨架,只允许改变某些具体步骤的实现。
  3. 重构时,将相同的代码抽取到父类中,形成模板方法,提高代码的复用性。

模板方法模式的优势

  1. 代码复用: 模板方法模式将相同的算法骨架封装在父类中,提高了代码的复用性。
  2. 封装变化: 模板方法模式允许子类在不改变算法结构的情况下重新定义某些步骤的实现,封装了变化。
  3. 简化子类: 子类不需要关心算法的结构,只需实现具体步骤,简化了子类的设计。

使用示例

1
2
3
4
5
6
7
8
9
10
11
class Program
{
static void Main()
{
// 创建具体类对象
ConcreteClass concreteClass = new ConcreteClass();

// 调用模板方法,实际上会执行具体类中定义的算法骨架
concreteClass.TemplateMethod();
}
}

总结

模板方法模式是一种行为型设计模式,通过定义一个算法的骨架,将算法中的一些步骤延迟到子类中实现。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤,提高了代码的复用性和灵活性。在实际应用中,模板方法模式常用于多个子类有共同的算法骨架,但其中某些步骤的实现可能不同的场景。

命令模式 - 行为型模式的请求发送者与接收者分离者

在软件设计中,命令模式是一种行为型设计模式,它将请求封装为一个对象,使得可以参数化客户端对象,队列中存储请求,并且支持撤销操作。命令模式将请求的发送者和接收者解耦,使得系统更加灵活。本文将深入讨论命令模式的概念、实现方式以及在实际应用中的使用场景。

命令模式的概念

命令模式(Command Pattern)是一种行为型设计模式,其核心思想是将请求封装为一个对象,使得可以参数化客户端对象,队列中存储请求,并支持撤销操作。命令模式将请求的发送者和接收者解耦,使得系统更加灵活,能够支持命令的合成和扩展。

命令模式的 UML 类图

classDiagram
    class Command {
        + Execute(): void
        + Undo(): void
    }

    class ConcreteCommand {
        - receiver: Receiver
        + Execute(): void
        + Undo(): void
    }

    class Receiver {
        + Action(): void
    }

    class Invoker {
        - command: Command
        + SetCommand(command: Command): void
        + ExecuteCommand(): void
        + UndoCommand(): void
    }

    class Client

    Command <|.. ConcreteCommand
    ConcreteCommand --> Receiver
    Invoker --> Command

命令模式的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using System;

// 命令接口
public interface Command
{
void Execute();
void Undo();
}

// 具体命令类
public class ConcreteCommand : Command
{
private Receiver receiver;

public ConcreteCommand(Receiver receiver)
{
this.receiver = receiver;
}

public void Execute()
{
receiver.Action();
}

public void Undo()
{
// 提供撤销操作的具体实现
}
}

// 接收者类
public class Receiver
{
public void Action()
{
Console.WriteLine("Receiver Action");
}
}

// 请求发送者类
public class Invoker
{
private Command command;

public void SetCommand(Command command)
{
this.command = command;
}

public void ExecuteCommand()
{
command.Execute();
}

public void UndoCommand()
{
command.Undo();
}
}

命令模式的应用场景

命令模式适用于以下情况:

  1. 需要将请求的发送者和接收者解耦,使得系统更加灵活。
  2. 需要支持请求的撤销操作。
  3. 需要支持命令的合成和扩展。
  4. 需要在不同的时间指定请求、将请求排队、记录请求等。

命令模式的优势

  1. 解耦性: 命令模式将请求的发送者和接收者解耦,使得系统更加灵活,易于扩展和维护。
  2. 可扩展性: 可以方便地增加新的命令类,实现对命令的扩展。
  3. 支持撤销操作: 可以实现命令的撤销操作,提供系统的可靠性。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
static void Main()
{
// 创建接收者对象
Receiver receiver = new Receiver();

// 创建具体命令对象,传入接收者对象
Command command = new ConcreteCommand(receiver);

// 创建请求发送者对象,传入具体命令对象
Invoker invoker = new Invoker();
invoker.SetCommand(command);

// 发送请求,实际上会执行接收者对象的操作
invoker.ExecuteCommand();
}
}

总结

命令模式是一种行为型设计模式,通过将请求封装为一个对象,实现了请求的发送者和接收者的解耦。命令模式支持撤销操作,并且易于扩展和维护。在实际应用中,命令模式常用于实现撤销、排队、记录日志等功能。

解释器模式 - 行为型模式的语言解释者

在软件设计中,解释器模式是一种行为型设计模式,它定义了一种语言文法的表示,并提供了解释器来解释该语言中的句子。解释器模式可以用于实现自定义语言、配置文件解析等场景。本文将深入讨论解释器模式的概念、实现方式以及在实际应用中的使用场景。

解释器模式的概念

解释器模式(Interpreter Pattern)是一种行为型设计模式,其核心思想是定义一种语言文法的表示,并提供一个解释器来解释该语言中的句子。解释器模式主要用于实现自定义语言、配置文件解析、正则表达式等场景,通过定义语法规则和解释器,实现对特定语言的解释和执行。

解释器模式的 UML 类图

classDiagram
    class Context {
        - input: string
        + Context(input: string)
        + GetNextToken(): string
        + GetToken(): string
    }

    class AbstractExpression {
        + Interpret(context: Context): void
    }

    class TerminalExpression {
        + Interpret(context: Context): void
    }

    class NonterminalExpression {
        + Interpret(context: Context): void
    }

    Context --> AbstractExpression
    AbstractExpression <|-- TerminalExpression
    AbstractExpression <|-- NonterminalExpression

解释器模式的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using System;

// 上下文类,保存解释器需要的信息
public class Context
{
private readonly string input;
private int currentTokenIndex;

public Context(string input)
{
this.input = input;
this.currentTokenIndex = 0;
}

// 获取下一个标记
public string GetNextToken()
{
// 简化示例,实际中可能需要更复杂的逻辑
if (currentTokenIndex < input.Length)
{
return input[currentTokenIndex++].ToString();
}
return null;
}

// 获取当前标记
public string GetToken()
{
// 简化示例,实际中可能需要更复杂的逻辑
if (currentTokenIndex < input.Length)
{
return input[currentTokenIndex].ToString();
}
return null;
}
}

// 抽象表达式类
public abstract class AbstractExpression
{
public abstract void Interpret(Context context);
}

// 终结符表达式类
public class TerminalExpression : AbstractExpression
{
public override void Interpret(Context context)
{
string token = context.GetToken();
if (token != null)
{
Console.WriteLine($"Terminal Expression interprets token: {token}");
}
}
}

// 非终结符表达式类
public class NonterminalExpression : AbstractExpression
{
public override void Interpret(Context context)
{
string token = context.GetToken();
if (token != null)
{
Console.WriteLine($"Nonterminal Expression interprets token: {token}");
}
}
}

解释器模式的应用场景

解释器模式适用于以下情况:

  1. 需要实现自定义语言,且语言规则相对简单。
  2. 需要解析和执行配置文件,例如 XML 配置文件。
  3. 需要实现特定领域的脚本语言,如数学表达式解析。

解释器模式的优势

  1. 灵活性: 解释器模式允许动态改变语言规则,灵活适应不同的需求。
  2. 可扩展性: 可以轻松地扩展语言中的语法规则,增加新的表达式类型。
  3. 可维护性: 解释器模式将语法规则和解释过程分离,易于维护和扩展。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Program
{
static void Main()
{
// 创建上下文对象,传入待解释的语言
Context context = new Context("A B C D");

// 创建终结符和非终结符表达式对象
AbstractExpression terminalExpression = new TerminalExpression();
AbstractExpression nonterminalExpression = new NonterminalExpression();

// 解释语言中的每个标记
while (context.GetToken() != null)
{
// 判断标记类型,选择不同的表达式进行解释
if (context.GetToken() == "A" || context.GetToken() == "B")
{
terminalExpression.Interpret(context);
}
else
{
nonterminalExpression.Interpret(context);
}
}
}
}

总结

解释器模式是一种行为型设计模式,通过定义一种语言文法的表示,并提供一个解释器来解释该语言中的句子。解释器模式主要用于实现自定义语言、配置文件解析等场景,通过定义语法规则和解释器,实现对特定语言的解释和执行。在实际应用中,解释器模式需要根据具体场景灵活设计语法规则和解释器,以满足不同领域的需求。