随便写写,用于记录,工厂方法

本文详细介绍了三种面向对象的设计模式:工厂方法用于延迟对象创建,降低类间的耦合;原型模式通过克隆快速复制已有对象,减少重复创建;构建器模式则在复杂对象构建时提供灵活的构建过程。这些模式在实际开发中能够提高代码的可维护性和扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对象设计模式

工厂方法

例如在在一个类A中, 例如我需要在运行的过程中创建令一个类的对象。但是如果直接NEW对象 B,那么这个B就和这个A产生了强依赖,A在编译的时候就依赖B了
例如如下代码:

#include <iostream>

#include <map>

class splitter{
public:
  virtual void split() = 0;课程
};

class TxtSplitter : public splitter{
  void split() override{
    std::cout << " 1" << std::endl;
  }
};

class MusicSplitter : public splitter{
  void split() override{
    std::cout << "2" << std::endl;
  }
};

class Button {
public:
  Button(){
  }

  void click(){
    splitter* spl = new MusicSplitter();
    spl ->split();
    delete(spl);
  }

private:
};

int main() {
  auto button =  Button();
  button.click();
}

例如我有一个类Button, 在用户点击按钮之后会调用click方法,之后这个函数需要将用户选择的内容分解为数个部分。用户选择的内容可能是音乐,文件等。 所以很容易想到将 split抽象出一个方法, 例如上述代码中的 class splitter 。 TxtSplitter 和 MusicSplitter分别实现文件分割和音乐的分割。之后在 class Button 在是实例化相应的对象, 这样子看似可以,但实际上 Button 和 MusicSplitter 产生了强依赖。在编的编译的时候编译TxtSplitter 必须 需要MusicSplitter。

如何改进呢? 看下列的代码


class ISplitterFactory{
public:
  virtual ISplitter* CreateSplitterFactory() = 0;
};


class TxtSplitterFactory : public ISplitterFactory{
public:
  ISplitter* CreateSplitterFactory() override{
    return  new MusicSplitter();
  }
};

用一个抽象类将里面放一个方法用于去生成ISplitter对象。
之后的buottn的代码可以写成下面的样子:

class Button1 {ISplitterFactory
public:
  Button1(ISplitterFactory *sql_factory)
  : SqlFactory_(sql_factory)
  {
  }

  void click(){
  	//千万不可以写成这样子,如果写成这样子实际上又和上面一样了。
  	//ISplitter* spl = TxtSplitterFactory().CreateSplitterFactory();
    ISplitter* spl = SqlFactory_->CreateSplitterFactory();
    spl ->split();
    delete(spl);
  }

private:
  ISplitterFactory* SqlFactory_;
};

这样子,虽然最终编译的时候为们还是需要TxtSplitterFactory,但Button1实际上和前者已经没有强依赖了。
外部的函数可以写成这样子

int main() {
//  auto button =  Button();
//  button.click();
  TxtSplitterFactory* factory =  new TxtSplitterFactory(); //通过这个方法就可以将初始化的时间延续到运行时;
  Button1 a(factory);
  a.click();
}

上面的情况比较简单,只有一个步骤,但如果是一个很复杂的过程,例如:数据库的操作, 连接, 读取,解析一系列步骤,而每个步骤都需要一个新的对象。 那么就相当于每一个对象都需要一个工厂了,上面的三个步骤就相当于三个对象。 例如:


class SqlConnection : public IDbConnection{
public:
  void connection() override{
    std::cout << "connection" << std::endl;
  }
};

class SqlCommand : public IDbCommand{
  void execute() override{
    std::cout << "execute" << std::endl;
  }
};

class SqlReader : public IDataReader{
  void reader() override{
    std::cout << "read" << std::endl;
  }
};

加入要支持 influxdb 那么就需要在写三个。
但是这样有一个问题SqlConnection对象的结果只能用SqlCommand处理。 而不能用influxdbCommand。 这样子我们可以写的更好一点

class IDBFactory{
public:
  virtual IDbConnection* CreateDbConnection() =0;
  virtual IDbCommand* CreateDbCommand() = 0;
  virtual IDataReader* CreateDbReader() = 0;
};


class EmployDao{
  IDBFactory* dbFactory;
public:
  std::vector<std::string> GetEmploy(){
    IDbConnection* connection = dbFactory->CreateDbConnection();
    //...
    IDbCommand* cmd = dbFactory->CreateDbCommand();
    //...
    IDataReader* reader = dbFactory->CreateDbReader();
  }
};

一个这样的工厂,这样子就将同一类的封装起来了。 通过一个工厂生成多个不同的对象。

proptype 原型对象

如果一个类有很多中间状态,创建之后如果要经过一两个阶段之后才可以使用,那么对于这样的类可以使用原型模式, 通过clone的方法直接将这个对象拷贝出来
例如:

class MainForm {
private:
  ISplitter1* prototype_;
public:
  MainForm(ISplitter1* prototype){
    this->prototype_ = prototype;
  }

  void Button_click(){
    ISplitter1* splitter = prototype_->clone();
    splitter->split();
    delete(splitter);
  }
};

上面的类中的 ISplitter1* prototype_; 是专门用于创建对象用的,在这个设计模式中它里面的内容不应该被改变。如果或改变了就变味道了

builder 构建器模式

当面对复杂对象的创建工作,通常各自部分的子对象用一定的算法构成, 但是将他们组合在一起的算法却相对稳定
那么在父类中可以封装算法函数。在子类中实现各个步骤例如:

class House {
public:
  void init(){
    BuildPart1();
    for (int i = 0; i < 4; i++) {
      BuildPart2();
    }
    for (int i = 0; i < 2; i++){
      BuildPart3();
    }
  }
  ~House(){};
  protected:
  virtual void BuildPart1() = 0;
  virtual void BuildPart2() = 0;
  virtual void BuildPart3() = 0;
};

class BigHour : public House{
  virtual void BuildPart1(){
    std::cout << "1" << std::endl;
  }
  virtual void BuildPart2(){
    std::cout << "2" << std::endl;
  }
  virtual void BuildPart3(){
    std::cout << "3" << std::endl;
  }
};

int main(){
  House* p = new BigHour;
  p->init();
}

在构建BigHour的时候是,我们只需要重写三个虚函数即可。

class Hous{
public:
  std::string name;
};

class HouseBuilder {
public:
  HouseBuilder(Hous* p){
    pHous_ = p;
  }
  Hous* GetResult(){
    return pHous_;
  }

  virtual void BuildPart1() = 0;
  virtual void BuildPart2() = 0;
  virtual void BuildPart3() = 0;
  protected:
  Hous* pHous_;
};

class StoneHouseBuilder : public HouseBuilder{
public:
  StoneHouseBuilder(Hous* p)
  :HouseBuilder(p)
  {
  }

  virtual void BuildPart1(){
    pHous_->name.append("a");
  }
  virtual void BuildPart2(){
    pHous_->name.append("b");
  }
  virtual void BuildPart3(){
    pHous_->name.append("c");
  }
private:
};

class HourDirector{
  HouseBuilder* pHouseBuilder;
public:
  HourDirector(HouseBuilder* p){
    pHouseBuilder = p;
  }

  Hous* Construct(){
    pHouseBuilder->BuildPart1();
    for (int i = 0; i < 4; ++i) {
      pHouseBuilder->BuildPart2();
    }
    pHouseBuilder->BuildPart3();
    return pHouseBuilder->GetResult();
  }
};


int main(){
  Hous a;
  StoneHouseBuilder builder(&a);
  HourDirector director(&builder);
  std::cout <<  director.Construct()->name << std::endl;
}

还有一个最终版本的,将房子,方法,构建步骤都进行区分的方法:
Hous 用于这个对象的上下文,HouseBuilder是每个的步骤,HourDirector用于封装步骤

<think>好的,用户之前问了关于Spring Cloud Gateway的路由转发,特别是ID和断言的作用。现在用户的新问题是:“id我可以随便写吗?”需要仔细思考如何回答。 首先,回顾之前的对话,用户已经知道ID是路由的唯一标识符,用于管理和操作路由配置。现在用户可能想知道在设置ID时是否有约束或最佳实践。虽然技术上说,ID可以是任意字符串,但实际应用中可能有需要注意的地方。 接下来,分析用户的问题。用户可能是在配置路由时不确定ID的命名是否有规则,或者担心随意命名会带来问题。需要明确回答是否可以随意编写,同时给出建议,帮助用户避免潜在的问题。 然后,考虑技术细节。Spring Cloud Gateway的ID确实没有严格的格式要求,但必须唯一。如果重复,会导致配置错误,路由无法正确加载或覆盖。此外,好的ID命名能提高可读性和维护性,尤其是在大型项目中。 还要思考用户可能的场景。用户可能是在开发或维护微服务架构,需要管理多个路由。他们可能希望了解如何有效管理这些路由,避免冲突,方便后续的调试和扩展。 需要指出可能的误区。虽然可以随便写,但不建议使用无意义的名称,比如“abc”或“123”,这样在查找或修改时会困难。应该使用有意义的命名,比如结合服务名称和路由目的。 还要考虑用户可能未提及的深层需求,比如动态路由的管理,或者通过API修改路由配置时ID的重要性。在这种情况下,唯一且有意义的ID会大大简化操作。 最后,总结回答的结构:首先明确可以自由命名,但必须唯一;然后解释可能的问题;接着给出建议和最佳实践;最后通过示例说明正确和错误的做法,帮助用户理解。</think>### Spring Cloud Gateway 路由ID的命名规则与注意事项 #### 1. **ID是否可以随便写?** **技术层面**:ID允许自由命名,但需满足以下条件: - ✅ **唯一性**:同一网关实例中不允许重复ID - ✅ **字符串格式**:仅支持字母、数字、下划线和短横线(`a-z`/`0-9`/`_`/`-`) - ❌ **禁用特殊字符**:`#`, `$`, `空格`等会导致配置解析失败 #### 2. **随意命名的潜在问题** | 错误示例 | 可能后果 | |----------------|-----------------------------| | `id: 123` | 数字开头可能导致配置解析异常 | | `id: order%` | 特殊符号引发YAML语法错误 | | 重复ID | 后定义的路由覆盖先前的配置 | #### 3. **最佳实践** - **语义化命名**:体现路由功能(例:`user_auth_route` > `route1`) - **统一规范**(推荐格式): ```yaml id: {服务名}_{功能}_route # 例:payment_wechat_route id: {业务模块}_api_route # 例:inventory_query_route ``` - **动态路由场景**:结合UUID或时间戳保证唯一性(例:`order_route_66d4e7f8`) #### 4. **配置错误示例分析** ```yaml # ❌ 错误配置 routes: - id: 2023_route # 数字开头 uri: lb://serviceA - id: auth#test # 包含特殊符号 uri: lb://serviceB # ✅ 正确配置 routes: - id: order_service_v1_route uri: lb://order-service - id: payment_alipay_redirect uri: lb://payment-service ``` #### 5. **ID的核心作用** - **路由管理**:通过`Actuator API`动态修改/删除路由(依赖ID定位) ```bash # 删除指定路由 DELETE /actuator/gateway/routes/{id} ``` - **日志追踪**:在网关日志中快速定位问题路由 - **监控指标**:Prometheus等监控工具按ID统计路由流量 #### 总结 **可以自由命名但需遵循规范**,良好的ID设计能显著提升: - 配置可读性 - 系统可维护性 - 问题排查效率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值