百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程文章 > 正文

初级开发人员告诉我:OO 设计模式太复杂而且没用

qiyuwang 2025-03-25 17:06 11 浏览 0 评论

我从我们的谈话中学到了很多东西。

对话

几周前,午餐时间。

我的学弟告诉我:“我认为面向对象编程没有意义,它总是在复杂的事情上。”

“你为什么这么认为?” 我问。

伙计:“你看,即使是为了一些简单的任务,它也总是需要创建一堆类。 我不喜欢被迫创建一些类来完成事情的想法”。 “例如,你看到我们的密码验证器逻辑,一个函数就足够了,为什么还需要一个类?”

我:“同意。 你不必这样做。 但类层次结构就像抽象工具。 将其视为数据+函数“包”的抽象。 需要时使用它们,如果不需要,就留下它们。 继续你认为简单的方式”。

伙计:“是的,但我不明白为什么要发明这个类的想法,OO 设计模式是做什么用的? 这么复杂,还有DI/IOC,好像没有必要”,“那么,什么是MVC模式? 我们正在使用的 Web 框架?”,“我喜欢函数式编程完成工作的方式,它非常简单且副作用更少”。

我:“你问的是不同的事情,给我一些时间来巩固答案,我保证会把链接发给你”。

为了把详细信息返回给我的兄弟。 我问自己第一个问题:“面向对象编程的核心思想是什么”。

面向对象编程

我记得一些著名的 OO 模式,如模板方法、工厂、单例、责任链和装饰器。

他们都在尝试做一些共同的事情——控制。

模板法。 主流程控制。 派生具体类中的“步骤”实现。

#include 
#include 
class View { // AbstractClass
public:
  // defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm.
  virtual void doDisplay() {}
  // implements a template method defining the skeleton of an algorithm. The template method calls primitive operations as well as operations defined in AbstractClass or those of other objects.
  void display() {
    setFocus();
    doDisplay();
    resetFocus();
  }
  virtual ~View() = default;
private:
  void setFocus() {
    std::cout << "View::setFocus\\n";
  }
  void resetFocus() {
    std::cout << "View::resetFocus\\n";
  }
};
class MyView : public View { // ConcreteClass
  // implements the primitive operations to carry out subclass-specific steps of the algorithm.
  void doDisplay() override {
    // render the view's contents
    std::cout << "MyView::doDisplay\\n";
  }
};
int main() {
  // The smart pointers prevent memory leaks
  std::unique_ptr myview = std::make_unique();
  myview->display();
}
  • 工厂 控制对象的创建。
#include 
#include 
enum ProductId {MINE, YOURS};
// defines the interface of objects the factory method creates.
class Product {
public:
  virtual void print() = 0;
  virtual ~Product() = default;
};
// implements the Product interface.
class ConcreteProductMINE: public Product {
public:
  void print() {
    std::cout << "this=" << this << " print MINE\\n";
  }
};
// implements the Product interface.
class ConcreteProductYOURS: public Product {
public:
  void print() {
    std::cout << "this=" << this << " print YOURS\\n";
  }
};
// declares the factory method, which returns an object of type Product.
class Creator {
public:
  virtual std::unique_ptr create(ProductId id) {
    if (ProductId::MINE == id) return std::make_unique();
    if (ProductId::YOURS == id) return std::make_unique();
    // repeat for remaining products...
    return nullptr;
  }
  virtual ~Creator() = default;
};
int main() {
  // The unique_ptr prevent memory leaks.
  std::unique_ptr creator = std::make_unique();
  std::unique_ptr product = creator->create(ProductId::MINE);
  product->print();
  product = creator->create(ProductId::YOURS);
  product->print();
}
  • 单例 控制有多少个对象(仅允许 1 个)穿过系统。
#include 
class Singleton {
public:
  static Singleton& get() {
    static Singleton instance;
    return instance;
  }
  int getValue() {
    return value;
  }
  void setValue(int value_) {
    value = value_;
  }
private:
  Singleton() = default;
  ~Singleton() = default;
  int value;
};
int main() {
  Singleton::get().setValue(42);
  std::cout << value='<< Singleton::get().getValue() << '\\n';
}
  • 责任链 控制请求的处理或过滤方式(允许多个处理程序)
#include 
#include 
typedef int Topic;
constexpr Topic NO_HELP_TOPIC = -1;
// defines an interface for handling requests.
class HelpHandler { // Handler
public:
  HelpHandler(HelpHandler* h = nullptr, Topic t = NO_HELP_TOPIC)
    : successor(h), topic(t) {}
  virtual bool hasHelp() {
    return topic != NO_HELP_TOPIC;
  }
  virtual void setHandler(HelpHandler*, Topic) {}
  virtual void handleHelp() {
    std::cout << helphandler::handlehelp\\n optional implements the successor link. if successor successor->handleHelp();
    }
  }
  virtual ~HelpHandler() = default;
  HelpHandler(const HelpHandler&) = delete; // rule of three
  HelpHandler& operator=(const HelpHandler&) = delete;
private:
  HelpHandler* successor;
  Topic topic;
};
class Widget : public HelpHandler {
public:
  Widget(const Widget&) = delete; // rule of three
  Widget& operator=(const Widget&) = delete;
protected:
  Widget(Widget* w, Topic t = NO_HELP_TOPIC) 
    : HelpHandler(w, t), parent(nullptr) {
    parent = w;
  }
private:
  Widget* parent;
};
// handles requests it is responsible for.
class Button : public Widget { // ConcreteHandler
public:
  Button(std::shared_ptr h, Topic t = NO_HELP_TOPIC) : Widget(h.get(), t) {}
  virtual void handleHelp() {
    // if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor.
    std::cout << "Button::handleHelp\\n";
    if (hasHelp()) {
      // handles requests it is responsible for.
    } else {      
      // can access its successor.
      HelpHandler::handleHelp();
    }
  }
};
class Dialog : public Widget { // ConcreteHandler
public:
  Dialog(std::shared_ptr h, Topic t = NO_HELP_TOPIC) : Widget(nullptr) {
    setHandler(h.get(), t);
  }
  virtual void handleHelp() {
    std::cout << "Dialog::handleHelp\\n";
    // Widget operations that Dialog overrides...
    if(hasHelp()) {
      // offer help on the dialog
    } else {
      HelpHandler::handleHelp();
    }
  }
};
class Application : public HelpHandler {
public:
  Application(Topic t) : HelpHandler(nullptr, t) {}
  virtual void handleHelp() {
    std::cout << "Application::handleHelp\\n";
    // show a list of help topics
  }
};
int main() {
  constexpr Topic PRINT_TOPIC = 1;
  constexpr Topic PAPER_ORIENTATION_TOPIC = 2;
  constexpr Topic APPLICATION_TOPIC = 3;
  // The smart pointers prevent memory leaks.
  std::shared_ptr application = std::make_shared(APPLICATION_TOPIC);
  std::shared_ptr dialog = std::make_shared(application, PRINT_TOPIC);
  std::shared_ptr

然后当我看IOC和DI时。 他们携手合作,也做同样的事情——控制:抽象应该依赖于哪个类?

OO的核心思想似乎就是控制。 我们使用接口或抽象类作为抽象规则或策略,然后使用类来实现不同的方式。 OO 的美妙之处在于控制(抽象)和业务逻辑(具体类)分离。

随着我越来越多的搜索,我发现罗伯特已经在这里发表了一篇论文。

那么接下来这个家伙的第二个问题,MVC 模式在 Web 框架中做什么?

MVC

当每个 HTTP 请求都经过平衡器然后路由到我们的应用程序时。 它需要一些一般处理。

比如我们需要解析 header 和 body,找出哪个函数或方法应该处理这个请求,并且数据是从查询字符串或表单正文或 JSON 正文或文件中获取的。

我们不想为每个新添加的类或函数一遍又一遍地执行此操作,这种处理可以通用化。

是的,它不一定是 MVC。

如果我们看看我们老式的 PHP、asp、jsp 或其他语言。 您可以从 PHP 中的 $server 或 asp 中的 Request[””] 获取标头值。 但还有一些工作需要推广:比如请求路由、过滤、视图模板引擎和模型反序列化。

这就是每个 MVC Web 框架可以给我们带来的东西。

因此,MVC 框架工作的答案是 — 带来更高级别的抽象来概括请求的处理。

是的,这又是关于控制。 一般而言,控制我们处理请求的方式。

泛型

这让我想起,当用 Java 和 C# 进行编码时,C++ 中的泛型类型或模板正在做完全相同的事情——抽象出类型处理,并使代码专注于业务逻辑本身。

让我们编写一个简单的函数来将数字加 1。

int inc(int n){
    return n+1
}

因此,如果我们想为 float 类型或 double 类型添加相同的函数,我们需要将上述函数重复 3 次。 如果我们想让它线程安全怎么办? 我们需要为以上三个函数添加锁或同步(或使用互斥锁)。 我们不需要下面的代码。

int inc(int n){
    lock (obj){
        return n+1;
    }
}
float inc(float n){
    lock (obj){
        return n+1;
    }
}
...

所以这里需要对类型泛化进行控制。 这就是泛型发挥作用的地方。

T inc(T n) {
    lock (obj){
        return n+1;
    }
}

因此,使用上述一种版本的代码就可以满足所有要求。

那么你是否也看到了同样的事情呢? 是的,控制。 控制(类型泛化)和业务逻辑(增加、类型安全)的分离。

我哥们喜欢FP。 我们也去那里吧。

函数式编程

让我们尝试通过讨论这三个著名的函数来触及 FP 的核心思想:map、reduce 和 filter。

因此,如果我们想要将字符串数组中的所有偶数相乘 ['1','2','3','4','5','6']。 这能做什么?

程序方式:

s = 0
for a in array:
    a = int(a)
    if a%2 == 0:
        s+=a
return s

函数式的方法:

reduce(lambda a,b: a*b, filter(lambda x:x%2==0, map(int, ['1','2','3','4','5','6']) ) )

正如你所看到的。

从 string 到 int 的类型转换由 map 函数处理

偶数由过滤函数处理

通过先乘后求和来减少函数

所有这些功能都是通过管道传输的

所以是的。 你明白了。 我们仍然看到同样的事情! 控制和业务逻辑。

函数(map、reduce、filter)就像控制(如何、数据转换流程)

lambda 是业务逻辑(什么,乘以偶数)

在 FP 中,控制侧重于“如何”,而业务逻辑则侧重于“做什么”。

看看优雅的管道! 是的,出于同样的原因,UNIX shell 也很漂亮。 还因为每个“管道”都同意仅处理文本(KISS)。

既然我们谈论了 shell,那么让我们稍微深入一下。

DSL(领域特定语言)

外壳是DSL。

它构建了一个层来允许开发人员(尤其是 DevOps 人员)有效地完成工作。 就像 SQL 如何帮助 DBA 人员一样,Shell 也做了类似的工作。

如果你看一下DSL,它是一个抽象层。 它可以是一种迷你语言,使我们的开发人员能够轻松地与 UNIX(Shell、AWK)或数据库(SQL)或“大数据系统”(HIVE)“对话”; 或一些语言解析器,如 JIRa (JQL) 和 Kibana(KQL); 有时它们是编程语言运行时(JVM、CLR)或解释器本身(Perl、Python); 它不一定是解析器或解释器,这里也有 OO 世界中的解释器模式。

所以这是一个想法。

DSL 是一个中间层,使人类和系统之间的“通信”变得容易,在系统级别或代码级别。

所以,再说一遍,作为抽象本身,DSL 是控制层,而用法是业务逻辑(例如 SQL 查询、shell、awk 代码逻辑)。

是的,分离控制和业务逻辑。 再次。


密码验证代码示例

程序方式:

def validate(pwd):
    if len(pwd) < 6:
        return False
    if not has_special_chars(pwd):
        return False
    if used_in_last_2_month(pwd):
        return False

面向对象:

interface IValidator{
    bool validate(string pwd);
}
class SpecialCharValidator:IValidator {}
class PwdHistoryValidator:IValidator {}
class PwdLengthValidator:IValidator {}
class StrongPasswordValidator{
     public (Ienumerate validators) {
         ...
     }
     public validate(string pwd){
         for (var v in validators){
             if (!v.validate(pwd)){
                 return false;
             }
          return true;
     }
}

DSL 方式:

spec {
'min_len': 8,
'used_pwd_max_days': 60,
'special_chars': '!@#$%^&*()'
}
class pwd_parser:
    def __init__(spec):
        load_parser(spec)
    ...
    def run() -> bool:

def validate(pwd) -> bool:
    return pwd_parser(spec).run()

如果我进行代码审查,我将批准该代码的所有 3 个版本。

因为这正是我们想要表达抽象的方式。 这就是如何分离控制和业务逻辑。

使用接口或抽象类作为控制和业务逻辑转到具体类。 使用面向对象编程。

通用函数(如 map、reduce、filter、decorator、walk、permutation、combination 等)或 DSL + 解析器作为控制和 lambda 定义业务逻辑。 去FP。

程序方式。 控制和逻辑混合在一起,也很好。 只要代码足够小。

没有所谓的完美方法来完成事情。 只要 :

我们关心我们正在编写的每一行代码

熟悉如何正确、干净地完成工作

可读且简单

做一个务实的程序员,足够好就够了。

致我的兄弟

伙计,我想你正在读这篇文章,正如我们商定的那样。 我可以发布我们的对话与其他人分享! 感谢您提出的好问题,我学到了很多。

结论

作为开发人员,我们在日常工作中将控制和业务逻辑分开。 我们的界限越清晰,我们得到的代码就越好。

所以OO模式,IoC和DI; MVC 框架; 映射减少和过滤功能; DSL迷你语言,正在试图解决同样的问题——控制。

最后

算法 + 数据结构 = 程序

算法 = 逻辑 + 控制

所以,

程序=数据结构+逻辑+控制

谢谢阅读! 享受!

下一篇文章见。

相关推荐

基于Docker方式安装与部署Camunda流程引擎

1Camunda简介官网:https://docs.camunda.org/manual/7.19/installation/docker/Camunda是一个轻量级、开源且高度灵活的工作流和决策自...

宝塔Linux面板如何部署Java项目?(宝塔面板 linux)

通过宝塔面板部署Java还是很方便的,至少不需要自己输入tomcat之类的安装命令了。在部署java项目前,我还是先说下目前的系统环境,如果和我的系统环境不一样,导致部署不成功,那你可能需要去找其他资...

浪潮服务器如何用IPMI安装Linux系统

【注意事项】此处以浪潮服务器为例进行演示所需使用的软件:Chrome浏览器个人PC中需要预先安装java,推荐使用jdk-8u181-windows-x64.exe【操作步骤】1、在服务器的BIOS中...

Centos7环境Hadoop3集群搭建(hadoop集群环境搭建实验报告)

由于项目需要存储历史业务数据,经过评估数据量会达到100亿以上,在原有mongodb集群和ES集群基础上,需要搭建Hbase集群进行调研,所以首先总结一下Hadoop集群的搭建过程。一、三个节点的集群...

Hadoop高可用集群搭建及API调用(hadoop高可用原理)

NameNodeHA背景在Hadoop1中NameNode存在一个单点故障问题,如果NameNode所在的机器发生故障,整个集群就将不可用(Hadoop1中虽然有个SecorndaryNameNo...

使用Wordpress搭建一个属于自己的网站

现在开源的博客很多,但是考虑到wordpress对网站的seo做的很好,插件也多。并且全世界流量排名前1000万的网站有33.4%是用Wordpress搭建的!所以尝试用Wordpress搭建一个网站...

Centos 安装 Jenkins(centos 安装ssh)

1、Java安装查看系统是否已安装Javayumlistinstalled|grepjava...

Java教程:gitlab-使用入门(java中的git)

1导读本教程主要讲解了GitLab在项目的环境搭建和基本的使用,可以帮助大家在企业中能够自主搭建GitLab服务,并且可以GitLab中的组、权限、项目自主操作...

Dockerfile部署Java项目(docker部署java应用)

1、概述本文主要会简单介绍什么是Docker,什么是Dockerfile,如何安装Docker,Dockerfile如何编写,如何通过Dockerfile安装jar包并外置yaml文件以及如何通过do...

如何在Eclipse中搭建Zabbix源码的调试和开发环境

Zabbix是一款非常优秀的企业级软件,被设计用于对数万台服务器、虚拟机和网络设备的数百万个监控项进行实时监控。Zabbix是开放源码和免费的,这就意味着当出现bug时,我们可以很方便地通过调试源码来...

Java路径-02-Java环境配置(java环境搭建及配置教程)

1Window环境配置1.1下载...

35.Centos中安装python和web.py框架

文章目录前言1.Centos7python:2.Centos8python:3.进行下载web.py框架然后应用:4.安装好之后进行验证:5.总结:前言...

《我的世界》服务器搭建(我的世界服务器如何搭建)

1.CentOS7环境1.1更改YUM源#下载YUM源文件curl-o/etc/yum.repos.d/CentOS-Base.repohttps://mirrors.aliyun.com...

CentOS 7 升级 GCC 版本(centos7.4升级7.5)

1.GCC工具介绍GCC编译器:...

Linux安装Nginx详细教程(linux安装配置nginx)

环境准备1.因为Nginx依赖于gcc的编译环境,所以,需要安装编译环境来使Nginx能够编译起来。命令:yuminstallgcc-c++显示完毕,表示安装完成:2.Nginx的http模块需要...

取消回复欢迎 发表评论: