好的,以下是一篇符合要求的专业技术博客:
一、为什么我们需要重构过程式代码
老张最近接手了一个古老的PHP项目,打开文件一看差点晕过去——满屏的mysql_query和长达500行的过程式代码。这种"意大利面条式"代码就像把所有的衣服都扔进一个衣柜,找条袜子都得翻遍整个房间。
过程式代码的典型问题包括:
- 函数间高度耦合,改一个地方可能影响十处
- 全局变量满天飞,debug像侦探破案
- 重复代码随处可见,维护成本指数级增长
来看个真实案例(技术栈:PHP 7.4+MySQL):
// 用户注册的过程式实现
function registerUser($username, $password, $email) {
// 验证输入
if(empty($username)) {
die("用户名不能为空");
}
if(strlen($password) < 6) {
die("密码太短");
}
// 连接数据库
$conn = mysqli_connect("localhost", "root", "", "myapp");
if(!$conn) {
die("数据库连接失败");
}
// 检查用户名是否存在
$sql = "SELECT id FROM users WHERE username='".mysqli_real_escape_string($conn, $username)."'";
$result = mysqli_query($conn, $sql);
if(mysqli_num_rows($result) > 0) {
die("用户名已存在");
}
// 插入新用户
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (username, password, email) VALUES ('".
mysqli_real_escape_string($conn, $username)."', '".
$hashedPassword."', '".
mysqli_real_escape_string($conn, $email)."')";
if(!mysqli_query($conn, $sql)) {
die("注册失败: ".mysqli_error($conn));
}
// 记录日志
file_put_contents('register.log', date('Y-m-d H:i:s')." 新用户注册: ".$username."\n", FILE_APPEND);
return true;
}
这段代码暴露了过程式编程的典型缺陷:数据库操作、业务逻辑和错误处理全部混在一起,就像把酱油、醋和料酒都倒进一个瓶子里。
二、面向对象重构的基本原则
面向对象编程(OOP)就像整理衣柜,把T恤、裤子和袜子分别放在不同的抽屉里。重构时要遵循几个基本原则:
- 单一职责原则:一个类只做一件事
- 开闭原则:对扩展开放,对修改关闭
- 依赖倒置:依赖抽象而非实现
让我们用面向对象的方式重写上面的注册功能。首先创建数据库处理类:
class DatabaseConnection {
private $connection;
public function __construct($host, $user, $pass, $db) {
$this->connection = new mysqli($host, $user, $pass, $db);
if($this->connection->connect_error) {
throw new Exception("数据库连接失败: ".$this->connection->connect_error);
}
}
public function query($sql) {
$result = $this->connection->query($sql);
if(!$result) {
throw new Exception("查询失败: ".$this->connection->error);
}
return $result;
}
public function escape($value) {
return $this->connection->real_escape_string($value);
}
public function close() {
$this->connection->close();
}
}
这个类封装了所有数据库操作,就像专门管理数据库连接的管家。接下来创建用户服务类:
class UserService {
private $db;
private $logger;
public function __construct(DatabaseConnection $db, LoggerInterface $logger) {
$this->db = $db;
$this->logger = $logger;
}
public function register($username, $password, $email) {
$this->validateInput($username, $password);
if($this->usernameExists($username)) {
throw new Exception("用户名已存在");
}
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (username, password, email) VALUES ('".
$this->db->escape($username)."', '".
$hashedPassword."', '".
$this->db->escape($email)."')";
$this->db->query($sql);
$this->logger->log("新用户注册: ".$username);
return true;
}
private function validateInput($username, $password) {
if(empty($username)) {
throw new Exception("用户名不能为空");
}
if(strlen($password) < 6) {
throw new Exception("密码太短");
}
}
private function usernameExists($username) {
$sql = "SELECT id FROM users WHERE username='".$this->db->escape($username)."'";
$result = $this->db->query($sql);
return $result->num_rows > 0;
}
}
现在代码结构清晰多了,就像把衣服分类挂好的衣柜。每个类都有明确职责,数据库操作和业务逻辑完全分离。
三、进阶重构技巧:引入设计模式
当项目规模变大时,简单的OOP可能还不够。这时需要引入设计模式,就像给衣柜添加分隔板和收纳盒。
3.1 使用仓库模式管理数据访问
interface UserRepositoryInterface {
public function findUserByUsername($username);
public function save(User $user);
}
class UserRepository implements UserRepositoryInterface {
private $db;
public function __construct(DatabaseConnection $db) {
$this->db = $db;
}
public function findUserByUsername($username) {
$sql = "SELECT * FROM users WHERE username='".$this->db->escape($username)."'";
$result = $this->db->query($sql);
if($result->num_rows === 0) {
return null;
}
$data = $result->fetch_assoc();
return new User($data['id'], $data['username'], $data['password'], $data['email']);
}
public function save(User $user) {
$sql = "INSERT INTO users (username, password, email) VALUES ('".
$this->db->escape($user->getUsername())."', '".
$user->getPassword()."', '".
$this->db->escape($user->getEmail())."')";
$this->db->query($sql);
$user->setId($this->db->getLastInsertId());
}
}
3.2 实现依赖注入容器
class Container {
private $services = [];
public function register($name, callable $factory) {
$this->services[$name] = $factory;
}
public function get($name) {
if(!isset($this->services[$name])) {
throw new Exception("服务未注册: ".$name);
}
if(is_callable($this->services[$name])) {
$this->services[$name] = $this->services[$name]($this);
}
return $this->services[$name];
}
}
// 使用示例
$container = new Container();
$container->register('db', function() {
return new DatabaseConnection('localhost', 'root', '', 'myapp');
});
$container->register('logger', function() {
return new FileLogger('app.log');
});
$container->register('userService', function($c) {
return new UserService($c->get('db'), $c->get('logger'));
});
$userService = $container->get('userService');
四、重构实战:完整案例演示
让我们通过一个电商系统中的订单处理模块,展示完整重构过程。原始过程式代码如下:
// 过程式订单处理
function createOrder($userId, $items, $shippingAddress) {
$conn = mysqli_connect("localhost", "root", "", "shop");
// 验证用户
$userSql = "SELECT * FROM users WHERE id=".(int)$userId;
$userResult = mysqli_query($conn, $userSql);
if(mysqli_num_rows($userResult) === 0) {
die("用户不存在");
}
// 计算总价
$total = 0;
foreach($items as $item) {
$productSql = "SELECT price FROM products WHERE id=".(int)$item['product_id'];
$productResult = mysqli_query($conn, $productSql);
$product = mysqli_fetch_assoc($productResult);
$total += $product['price'] * $item['quantity'];
}
// 创建订单
$orderSql = "INSERT INTO orders (user_id, total, shipping_address, status)
VALUES (".(int)$userId.", $total, '".
mysqli_real_escape_string($conn, $shippingAddress)."', 'pending')";
if(!mysqli_query($conn, $orderSql)) {
die("创建订单失败");
}
$orderId = mysqli_insert_id($conn);
// 添加订单项
foreach($items as $item) {
$itemSql = "INSERT INTO order_items (order_id, product_id, quantity)
VALUES ($orderId, ".(int)$item['product_id'].", ".(int)$item['quantity'].")";
mysqli_query($conn, $itemSql);
}
// 发送通知
$user = mysqli_fetch_assoc($userResult);
mail($user['email'], "订单创建成功", "您的订单 #$orderId 已创建");
return $orderId;
}
重构后的面向对象版本:
class OrderService {
private $db;
private $userRepository;
private $productRepository;
private $orderRepository;
private $mailer;
public function __construct(
DatabaseConnection $db,
UserRepositoryInterface $userRepository,
ProductRepositoryInterface $productRepository,
OrderRepositoryInterface $orderRepository,
MailerInterface $mailer
) {
$this->db = $db;
$this->userRepository = $userRepository;
$this->productRepository = $productRepository;
$this->orderRepository = $orderRepository;
$this->mailer = $mailer;
}
public function createOrder($userId, array $items, $shippingAddress) {
// 验证用户
$user = $this->userRepository->find($userId);
if(!$user) {
throw new Exception("用户不存在");
}
// 计算总价
$total = $this->calculateTotal($items);
// 创建订单
$order = new Order();
$order->setUserId($userId);
$order->setTotal($total);
$order->setShippingAddress($shippingAddress);
$order->setStatus('pending');
$this->orderRepository->save($order);
// 添加订单项
foreach($items as $item) {
$orderItem = new OrderItem();
$orderItem->setOrderId($order->getId());
$orderItem->setProductId($item['product_id']);
$orderItem->setQuantity($item['quantity']);
$this->orderRepository->saveItem($orderItem);
}
// 发送通知
$this->mailer->send(
$user->getEmail(),
"订单创建成功",
"您的订单 #".$order->getId()." 已创建"
);
return $order->getId();
}
private function calculateTotal(array $items) {
$total = 0;
foreach($items as $item) {
$product = $this->productRepository->find($item['product_id']);
$total += $product->getPrice() * $item['quantity'];
}
return $total;
}
}
重构后的代码具有以下优势:
- 职责分离:数据库操作、业务逻辑和邮件发送各自独立
- 可测试性:每个组件都可以单独测试
- 可扩展性:添加新功能不会影响现有代码
- 可维护性:问题定位更快速准确
五、重构过程中的注意事项
在实际重构过程中,有几个关键点需要注意:
- 小步前进:每次只重构一小部分,确保系统始终可运行
- 测试保障:建立自动化测试套件,防止引入新bug
- 版本控制:频繁提交,便于回退
- 性能考量:面向对象可能带来轻微性能开销,但通常可以忽略
- 团队沟通:确保所有成员理解重构目的和方案
重构不是一蹴而就的过程,而是一个持续改进的旅程。就像整理房间一样,需要定期维护才能保持整洁。当你的代码变得越来越清晰、越来越灵活时,你会发现维护成本大幅降低,开发效率显著提升。
记住,好的代码不是写出来的,而是改出来的。重构不是可选项,而是每个负责任的开发者必须掌握的技能。从今天开始,试着用面向对象的思想重新审视你的老代码,你会发现一个全新的编程世界。
评论