常见架构模式的实现1、Pipe-Filter(管道过滤)模式

一、概述

Pipe-Filter 框架是一种软件设计模式,用于处理一系列数据处理步骤,将它们组合在一起以执行特定的任务。这种模式的核心思想是将任务分解成一系列独立的步骤,每个步骤被称为一个过滤器(Filter),并通过管道(Pipe)将它们连接在一起。每个过滤器负责特定的数据处理任务,而管道负责将数据从一个过滤器传递到下一个过滤器。

以下是 Pipe-Filter 框架的关键组成部分:

  1. 过滤器(Filter): 这是 Pipe-Filter 框架的基本构建块。每个过滤器都是一个独立的组件,负责执行特定的数据处理任务。它接收输入数据,经过处理后产生输出数据。过滤器应该是无状态的,以便可以更容易地组合和复用它们。

  2. 管道(Pipe): 管道负责连接过滤器,将一个过滤器的输出作为下一个过滤器的输入。通过管道,不同的过滤器可以协同工作,形成一个完整的数据处理流程。管道通常是一个抽象接口,定义了数据传递的方式,可以是同步的也可以是异步的。

  3. 数据流(Data Flow): 数据流是在过滤器之间流动的数据。它可以是离散的数据块,也可以是连续的数据流。过滤器通过处理数据流来完成特定的任务。

  4. 适配器(Adapter): 在实际应用中,数据的格式和处理方式可能会有差异。适配器用于将不同格式或不同接口的数据转换为适应过滤器的格式,确保数据能够在管道中正确地流动。

  5. 客户端(Client): 客户端负责配置和组装过滤器,创建管道,以及启动数据处理流程。客户端通常了解业务需求,并根据需求选择和配置适当的过滤器。

二、解释

Pipe-Filter(管道过滤)模式是一种软件架构模式,它将数据处理过程分解为一系列独立的处理步骤,每个步骤都是一个独立的过滤器。这些过滤器通过管道(或者队列)连接起来,前一个过滤器的输出作为后一个过滤器的输入,从而形成一个数据处理流水线。

这种架构模式的好处在于,它将复杂的数据处理任务分解为多个简单的步骤,每个步骤都可以独立开发、测试和维护。同时,由于每个过滤器只需关注自己的输入和输出,因此可以很容易地替换、重用或者增加新的过滤器,从而提高了系统的灵活性和可扩展性。

举个通俗的例子,可以把Pipe-Filter模式比喻为工厂中的流水线生产。每个工人(过滤器)负责流水线上的一个环节,比如清洗、组装、包装等。产品(数据)从流水线的一端进入,经过一系列工序(过滤器)处理,最终从流水线的另一端出来,完成了整个生产过程。这样的架构模式使得生产过程更高效、更易管理,也更容易调整和扩展。

三、示例:C、C++、Python、Go、Java

1、C

当使用C语言实现Pipe-Filter模式时,我们可以利用进程间通信来实现管道传输数据,并使用多个进程来完成不同的过滤操作。

下面是实现示例,我们将使用fork()创建子进程,使用pipe()创建管道进行进程间通信,然后在父子进程中分别进行数据过滤操作。

#include 
#include 
#include 

void filterA(int input_fd, int output_fd) {
    // 从input_fd读取数据,进行过滤操作,然后将结果写入output_fd
    // 这里只是一个示例,实际过滤操作可以是任何你需要的操作
    char buffer[1024];
    int bytes_read;
    while ((bytes_read = read(input_fd, buffer, sizeof(buffer))) > 0) {
        // 进行过滤操作,这里简单地将小写字母转换为大写字母
        for (int i = 0; i < bytes_read; i++) {
            if (buffer[i] >= 'a' && buffer[i] <= 'z') {
                buffer[i] -= 32;  // 转换为大写字母
            }
        }
        write(output_fd, buffer, bytes_read);
    }
}

void filterB(int input_fd, int output_fd) {
    // 从input_fd读取数据,进行过滤操作,然后将结果写入output_fd
    // 这里只是一个示例,实际过滤操作可以是任何你需要的操作
    char buffer[1024];
    int bytes_read;
    while ((bytes_read = read(input_fd, buffer, sizeof(buffer))) > 0) {
        // 进行过滤操作,这里简单地将偶数位置的字符删除
        for (int i = 0; i < bytes_read; i += 2) {
            buffer[i] = ' ';  // 将偶数位置的字符替换为空格
        }
        write(output_fd, buffer, bytes_read);
    }
}

int main() {
    int pipeA[2], pipeB[2];
    if (pipe(pipeA) == -1 || pipe(pipeB) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    pid_t pidA = fork();
    if (pidA == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (pidA == 0) {
        // 子进程A
        close(pipeA[1]);  // 关闭子进程A的写入端
        close(pipeB[0]);  // 关闭子进程A的读取端
        filterA(pipeA[0], pipeB[1]);
        close(pipeA[0]);
        close(pipeB[1]);
        exit(EXIT_SUCCESS);
    } else {
        pid_t pidB = fork();
        if (pidB == -1) {
            perror("fork");
            exit(EXIT_FAILURE);
        } else if (pidB == 0) {
            // 子进程B
            close(pipeA[0]);  // 关闭子进程B的读取端
            close(pipeB[1]);  // 关闭子进程B的写入端
            filterB(pipeB[0], pipeA[1]);
            close(pipeA[1]);
            close(pipeB[0]);
            exit(EXIT_SUCCESS);
        } else {
            // 父进程
            close(pipeA[0]);  // 关闭父进程的读取端
            close(pipeA[1]);  // 关闭父进程的写入端
            close(pipeB[0]);  // 关闭父进程的读取端
            close(pipeB[1]);  // 关闭父进程的写入端
            wait(NULL);
            wait(NULL);
            exit(EXIT_SUCCESS);
        }
    }
}

我们模拟了一个简单的Pipe-Filter模式,通过父子进程间的管道通信来实现数据的过滤操作。在这个示例中,我们创建了两个过滤函数filterA和filterB,分别用于进行不同的过滤操作。在主函数main中,我们使用fork()创建了两个子进程,分别用于执行filterA和filterB的过滤操作。通过pipe()创建了两个管道pipeA和pipeB,用于父子进程之间的通信。

在父进程中,我们关闭了不需要的管道端,并使用wait()等待子进程的结束。在子进程中,我们分别关闭了不需要的管道端,并执行了相应的过滤操作。

2、C++

当使用C++实现Pipe-Filter模式时,我们可以利用进程间通信来实现管道传输数据,并使用多个线程来完成不同的过滤操作。

下面是一个实现示例,我们将使用多线程和互斥锁来实现管道传输数据,并在不同的线程中进行数据过滤操作。

#include 
#include 
#include 
#include 
#include 

std::mutex mtx;
std::condition_variable cv;
std::queue<std::string> data_queue;

void filterA() {
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait(lck, []{ return !data_queue.empty(); });
    std::string data = data_queue.front();
    data_queue.pop();
    lck.unlock();

    // 进行过滤操作,这里只是一个示例,实际过滤操作可以是任何你需要的操作
    for (char &c : data) {
        if (c >= 'a' && c <= 'z') {
            c -= 32;  // 转换为大写字母
        }
    }

    std::cout << "Filter A: " << data << std::endl;
}

void filterB() {
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait(lck, []{ return !data_queue.empty(); });
    std::string data = data_queue.front();
    data_queue.pop();
    lck.unlock();

    // 进行过滤操作,这里只是一个示例,实际过滤操作可以是任何你需要的操作
    for (int i = 0; i < data.size(); i += 2) {
        data[i] = ' ';  // 将偶数位置的字符替换为空格
    }

    std::cout << "Filter B: " << data << std::endl;
}

int main() {
    std::thread t1(filterA);
    std::thread t2(filterB);

    // 模拟数据输入
    {
        std::lock_guard<std::mutex> lck(mtx);
        data_queue.push("hello");
        data_queue.push("world");
    }
    cv.notify_all();

    t1.join();
    t2.join();

    return 0;
}

在这个示例中,我们使用了std::mutex和std::condition_variable来实现线程间的同步和通信,实现了Pipe-Filter模式,通过多个线程的协作来完成数据的过滤操作。在filterA和filterB函数中,我们使用了条件变量cv来等待数据的到来,然后进行相应的过滤操作。在主函数main中,我们创建了两个线程t1和t2,分别用于执行filterA和filterB的过滤操作。

在主函数中,我们模拟了数据输入,将数据放入data_queue中,并通过条件变量cv.notify_all()通知等待的线程开始执行过滤操作。

3、Python

当使用Python实现Pipe-Filter模式时,我们可以利用多进程或者多线程来实现管道传输数据,并使用不同的进程或线程来完成数据过滤操作。

下面是一个实现示例,我们将使用多线程和队列来实现管道传输数据,并在不同的线程中进行数据过滤操作。

import threading
import queue

# 创建一个队列用于线程间通信
data_queue = queue.Queue()

# 过滤函数A,将小写字母转换为大写字母
def filterA():
    while True:
        data = data_queue.get()  # 从队列中获取数据
        filtered_data = data.upper()  # 将数据转换为大写字母
        print("Filter A:", filtered_data)
        data_queue.task_done()  # 通知队列数据处理完成

# 过滤函数B,删除偶数位置的字符
def filterB():
    while True:
        data = data_queue.get()  # 从队列中获取数据
        filtered_data = ''.join([data[i] if i % 2 != 0 else ' ' for i in range(len(data))])  # 删除偶数位置的字符
        print("Filter B:", filtered_data)
        data_queue.task_done()  # 通知队列数据处理完成

# 创建并启动过滤线程A
threadA = threading.Thread(target=filterA)
threadA.daemon = True  # 设置为守护线程,主线程结束时自动结束
threadA.start()

# 创建并启动过滤线程B
threadB = threading.Thread(target=filterB)
threadB.daemon = True  # 设置为守护线程,主线程结束时自动结束
threadB.start()

# 模拟数据输入
data_queue.put("hello")
data_queue.put("world")

# 等待所有数据处理完成
data_queue.join()

在这个示例中,我们使用了Python的多线程和队列来实现线程间的通信,实现了Pipe-Filter模式,通过多个线程的协作来完成数据的过滤操作。我们创建了两个过滤函数filterA和filterB,分别用于进行不同的过滤操作。在主程序中,我们创建了两个线程threadA和threadB,分别用于执行filterA和filterB的过滤操作。

在主程序中,我们通过队列data_queue来传输数据,并在每个过滤函数中使用data_queue.get()来获取数据,然后进行相应的过滤操作。过滤完成后,使用data_queue.task_done()通知队列数据处理完成。

4、Go

当使用Go语言实现Pipe-Filter模式时,我们可以利用goroutine和channel来实现管道传输数据,并使用不同的goroutine来完成数据过滤操作。

下面是一个实现示例,我们将使用goroutine和channel来实现管道传输数据,并在不同的goroutine中进行数据过滤操作。

package main

import (
	"fmt"
)

// 过滤函数A,将小写字母转换为大写字母
func filterA(input <-chan string, output chan<- string) {
	for data := range input {
		filteredData := ""
		for _, char := range data {
			if char >= 'a' && char <= 'z' {
				filteredData += string(char - 32) // 转换为大写字母
			} else {
				filteredData += string(char)
			}
		}
		output <- filteredData
	}
	close(output)
}

// 过滤函数B,删除偶数位置的字符
func filterB(input <-chan string, output chan<- string) {
	for data := range input {
		filteredData := ""
		for i, char := range data {
			if i%2 != 0 {
				filteredData += string(char)
			} else {
				filteredData += " " // 将偶数位置的字符替换为空格
			}
		}
		output <- filteredData
	}
	close(output)
}

func main() {
	// 创建两个通道用于线程间通信
	ch1 := make(chan string)
	ch2 := make(chan string)

	// 启动过滤函数A的goroutine
	go filterA(ch1, ch2)

	// 启动过滤函数B的goroutine
	go filterB(ch2, ch1)

	// 模拟数据输入
	data := []string{"hello", "world"}

	// 将数据发送到第一个过滤函数的通道
	for _, d := range data {
		ch1 <- d
	}
	close(ch1)

	// 从第二个过滤函数的通道接收处理后的数据
	for filteredData := range ch1 {
		fmt.Println("Filtered Data:", filteredData)
	}
}

在这个示例中,我们使用了Go语言的goroutine和channel来实现线程间的通信,实现了Pipe-Filter模式,通过多个goroutine的协作来完成数据的过滤操作。我们创建了两个过滤函数filterA和filterB,分别用于进行不同的过滤操作。在主程序中,我们创建了两个channel ch1和ch2,分别用于传输数据和处理后的数据。

在主程序中,我们使用go关键字启动了两个goroutine,分别用于执行filterA和filterB的过滤操作。然后将数据发送到第一个过滤函数的通道ch1中,经过filterA和filterB的处理后,从第二个过滤函数的通道ch1中接收处理后的数据,并打印出来。

5、Java

当使用Java实现Pipe-Filter模式时,我们可以利用多线程和阻塞队列来实现管道传输数据,并使用不同的线程来完成数据过滤操作。

下面是一个实现示例,我们将使用多线程和阻塞队列来实现管道传输数据,并在不同的线程中进行数据过滤操作。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

// 过滤器接口
interface Filter {
    void process(BlockingQueue<String> in, BlockingQueue<String> out) throws InterruptedException;
}

// 过滤器A,将小写字母转换为大写字母
class FilterA implements Filter {
    @Override
    public void process(BlockingQueue<String> in, BlockingQueue<String> out) throws InterruptedException {
        String data = in.take(); // 从输入队列中获取数据
        String filteredData = data.toUpperCase(); // 将数据转换为大写字母
        out.put(filteredData); // 将处理后的数据放入输出队列
    }
}

// 过滤器B,删除偶数位置的字符
class FilterB implements Filter {
    @Override
    public void process(BlockingQueue<String> in, BlockingQueue<String> out) throws InterruptedException {
        String data = in.take(); // 从输入队列中获取数据
        StringBuilder filteredData = new StringBuilder();
        for (int i = 0; i < data.length(); i++) {
            if (i % 2 != 0) {
                filteredData.append(data.charAt(i)); // 删除偶数位置的字符
            } else {
                filteredData.append(' '); // 将偶数位置的字符替换为空格
            }
        }
        out.put(filteredData.toString()); // 将处理后的数据放入输出队列
    }
}

public class PipeFilterPattern {
    public static void main(String[] args) {
        // 创建输入和输出队列
        BlockingQueue<String> inputQueue = new ArrayBlockingQueue<>(10);
        BlockingQueue<String> outputQueue = new ArrayBlockingQueue<>(10);

        // 创建过滤器A和过滤器B
        Filter filterA = new FilterA();
        Filter filterB = new FilterB();

        // 启动过滤器A的线程
        new Thread(() -> {
            try {
                filterA.process(inputQueue, outputQueue);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 启动过滤器B的线程
        new Thread(() -> {
            try {
                filterB.process(outputQueue, inputQueue);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 模拟数据输入
        try {
            inputQueue.put("hello");
            inputQueue.put("world");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 从输出队列中获取处理后的数据并打印
        try {
            System.out.println("Filtered Data: " + inputQueue.take());
            System.out.println("Filtered Data: " + inputQueue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个示例中,我们使用了Java的多线程和阻塞队列来实现线程间的通信,实现了Pipe-Filter模式,通过多个线程的协作来完成数据的过滤操作。我们创建了两个过滤器FilterA和FilterB,分别用于进行不同的过滤操作。在主程序中,我们创建了两个阻塞队列inputQueue和outputQueue,分别用于传输数据和处理后的数据。

在主程序中,我们创建了两个线程,分别用于执行filterA和filterB的过滤操作。然后将数据发送到第一个过滤器的输入队列inputQueue中,经过filterA和filterB的处理后,从第二个过滤器的输出队列inputQueue中获取处理后的数据,并打印出来。

你可能感兴趣的:(架构模式,架构,开发语言,后端)