第12章:面向对象基础
面向对象编程(Object-Oriented Programming,简称OOP)是现代编程中最重要的编程范式之一。它提供了一种组织和管理代码的方式,使程序更加模块化、可维护和可扩展。PHP从PHP 5开始完全支持面向对象编程。
学习目标
完成本章学习后,你将能够:
- 理解面向对象编程的基本概念和优势
- 掌握类和对象的定义与使用
- 学会创建和使用属性和方法
- 理解封装的概念和应用
- 掌握构造函数和析构函数的使用
- 学会基本的继承和扩展
- 能够使用面向对象的方式构建应用程序
什么是面向对象编程
编程范式的演进
从过程式编程到面向对象编程的转变:
<?php
// 过程式编程方式
function calculateArea($width, $height) {
return $width * $height;
}
function calculatePerimeter($width, $height) {
return 2 * ($width + $height);
}
// 使用过程式编程
$width = 10;
$height = 5;
$area = calculateArea($width, $height);
$perimeter = calculatePerimeter($width, $height);
echo "面积:$area, 周长:$perimeter\n";
?>
<?php
// 面向对象编程方式
class Rectangle {
private $width;
private $height;
public function __construct($width, $height) {
$this->width = $width;
$this->height = $height;
}
public function getArea() {
return $this->width * $this->height;
}
public function getPerimeter() {
return 2 * ($this->width + $this->height);
}
public function getWidth() {
return $this->width;
}
public function getHeight() {
return $this->height;
}
}
// 使用面向对象编程
$rectangle = new Rectangle(10, 5);
$area = $rectangle->getArea();
$perimeter = $rectangle->getPerimeter();
echo "面积:$area, 周长:$perimeter\n";
?>
面向对象的优势
- 模块化:将数据和操作数据的方法封装在一起
- 可重用性:通过类和对象实现代码重用
- 可维护性:代码结构清晰,易于修改和维护
- 可扩展性:通过继承和接口轻松扩展功能
- 安全性:通过访问控制保护数据安全
面向对象的基本概念
1. 对象(Object)
对象是面向对象编程的基本单位,是类的实例。对象包含:
- 属性(Properties):对象的数据或状态
- 方法(Methods):对象的行为或操作
2. 类(Class)
类是对象的模板或蓝图,定义了对象的属性和方法。
3. 封装(Encapsulation)
将数据和操作数据的方法捆绑在一起,隐藏内部实现细节。
4. 继承(Inheritance)
一个类可以继承另一个类的属性和方法。
5. 多态(Polymorphism)
不同对象对同一消息的不同响应。
现实世界中的面向对象概念
为了更好地理解面向对象,让我们用现实世界的例子来说明:
<?php
// 汽车类
class Car {
// 属性:汽车的特征
private $brand; // 品牌
private $model; // 型号
private $color; // 颜色
private $speed; // 当前速度
private $isRunning; // 是否在运行
// 构造方法:创建汽车时初始化
public function __construct($brand, $model, $color) {
$this->brand = $brand;
$this->model = $model;
$this->color = $color;
$this->speed = 0;
$this->isRunning = false;
echo "创建了一辆 {$this->color} 的 {$this->brand} {$this->model}\n";
}
// 方法:汽车的行为
public function start() {
if (!$this->isRunning) {
$this->isRunning = true;
echo "汽车启动了!\n";
} else {
echo "汽车已经在运行中!\n";
}
}
public function stop() {
if ($this->isRunning) {
$this->speed = 0;
$this->isRunning = false;
echo "汽车停止了!\n";
} else {
echo "汽车已经停止了!\n";
}
}
public function accelerate($amount) {
if ($this->isRunning) {
$this->speed += $amount;
echo "加速到 {$this->speed} km/h\n";
} else {
echo "请先启动汽车!\n";
}
}
public function brake($amount) {
if ($this->speed >= $amount) {
$this->speed -= $amount;
echo "减速到 {$this->speed} km/h\n";
} else {
$this->speed = 0;
echo "汽车已经停止\n";
}
}
// 获取器方法
public function getBrand() {
return $this->brand;
}
public function getModel() {
return $this->model;
}
public function getColor() {
return $this->color;
}
public function getSpeed() {
return $this->speed;
}
public function isRunning() {
return $this->isRunning;
}
// 设置器方法
public function setColor($color) {
$this->color = $color;
echo "汽车颜色改为 {$color}\n";
}
// 获取汽车信息
public function getInfo() {
$status = $this->isRunning ? "运行中" : "停止";
return "这是一辆 {$this->color} 的 {$this->brand} {$this->model},当前{$status},速度 {$this->speed} km/h";
}
}
// 使用汽车类
echo "=== 创建和使用汽车 ===\n";
$myCar = new Car("丰田", "卡罗拉", "白色");
echo $myCar->getInfo() . "\n\n";
$myCar->start();
$myCar->accelerate(50);
$myCar->accelerate(30);
$myCar->brake(20);
$myCar->stop();
echo "\n" . $myCar->getInfo() . "\n";
?>
面向对象 vs 过程式编程对比
让我们通过一个实际的例子来对比两种编程方式:
学生管理系统 - 过程式方式
<?php
// 数据存储
$students = [];
// 添加学生函数
function addStudent(&$students, $name, $age, $grade) {
$student = [
'id' => count($students) + 1,
'name' => $name,
'age' => $age,
'grade' => $grade
];
$students[] = $student;
return count($students);
}
// 获取学生信息函数
function getStudent($students, $id) {
foreach ($students as $student) {
if ($student['id'] == $id) {
return $student;
}
}
return null;
}
// 更新学生年龄函数
function updateStudentAge(&$students, $id, $newAge) {
foreach ($students as &$student) {
if ($student['id'] == $id) {
$student['age'] = $newAge;
return true;
}
}
return false;
}
// 计算平均年龄函数
function calculateAverageAge($students) {
if (empty($students)) {
return 0;
}
$totalAge = 0;
foreach ($students as $student) {
$totalAge += $student['age'];
}
return $totalAge / count($students);
}
// 使用过程式方式
echo "=== 过程式编程方式 ===\n";
$studentId1 = addStudent($students, "张三", 20, "一年级");
$studentId2 = addStudent($students, "李四", 19, "一年级");
$studentId3 = addStudent($students, "王五", 21, "二年级");
updateStudentAge($students, $studentId2, 20);
$student = getStudent($students, $studentId1);
if ($student) {
echo "学生信息:{$student['name']}, {$student['age']}岁, {$student['grade']}\n";
}
$avgAge = calculateAverageAge($students);
echo "平均年龄:" . round($avgAge, 1) . "岁\n";
?>
学生管理系统 - 面向对象方式
<?php
class Student {
private static $nextId = 1;
private $id;
private $name;
private $age;
private $grade;
private static $students = [];
public function __construct($name, $age, $grade) {
$this->id = self::$nextId++;
$this->name = $name;
$this->age = $age;
$this->grade = $grade;
self::$students[$this->id] = $this;
echo "创建了学生:{$this->name} (ID: {$this->id})\n";
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getAge() {
return $this->age;
}
public function setAge($age) {
if ($age > 0 && $age < 150) {
$this->age = $age;
return true;
}
return false;
}
public function getGrade() {
return $this->grade;
}
public function setGrade($grade) {
$this->grade = $grade;
}
public function getInfo() {
return "学生 {$this->name},{$this->age}岁,{$this->grade}";
}
// 静态方法:获取所有学生
public static function getAllStudents() {
return self::$students;
}
// 静态方法:根据ID获取学生
public static function getById($id) {
return self::$students[$id] ?? null;
}
// 静态方法:计算平均年龄
public static function calculateAverageAge() {
if (empty(self::$students)) {
return 0;
}
$totalAge = 0;
foreach (self::$students as $student) {
$totalAge += $student->getAge();
}
return $totalAge / count(self::$students);
}
// 静态方法:获取学生总数
public static function getTotalCount() {
return count(self::$students);
}
// 静态方法:根据年级获取学生
public static function getByGrade($grade) {
$gradeStudents = [];
foreach (self::$students as $student) {
if ($student->getGrade() === $grade) {
$gradeStudents[] = $student;
}
}
return $gradeStudents;
}
// 析构方法
public function __destruct() {
echo "学生对象 {$this->name} 已销毁\n";
}
}
// 使用面向对象方式
echo "\n=== 面向对象编程方式 ===\n";
$student1 = new Student("张三", 20, "一年级");
$student2 = new Student("李四", 19, "一年级");
$student3 = new Student("王五", 21, "二年级");
// 修改学生信息
$student2->setAge(20);
// 获取学生信息
$foundStudent = Student::getById(1);
if ($foundStudent) {
echo $foundStudent->getInfo() . "\n";
}
// 计算平均年龄
$avgAge = Student::calculateAverageAge();
echo "平均年龄:" . round($avgAge, 1) . "岁\n";
echo "学生总数:" . Student::getTotalCount() . "\n";
// 获取一年级学生
$grade1Students = Student::getByGrade("一年级");
echo "一年级学生数量:" . count($grade1Students) . "\n";
?>
面向对象的设计原则
1. 单一职责原则(SRP)
一个类应该只有一个引起变化的原因。
<?php
// 好的设计:职责分离
class Logger {
public function log($message) {
echo "[LOG] " . date('Y-m-d H:i:s') . " - $message\n";
}
}
class Calculator {
public function add($a, $b) {
return $a + $b;
}
public function subtract($a, $b) {
return $a - $b;
}
}
// 使用
$logger = new Logger();
$calculator = new Calculator();
$result = $calculator->add(10, 5);
$logger->log("计算结果:$result");
?>
2. 开闭原则(OCP)
软件实体应该对扩展开放,对修改关闭。
<?php
// 基础形状类
abstract class Shape {
abstract public function getArea();
}
// 圆形类
class Circle extends Shape {
private $radius;
public function __construct($radius) {
$this->radius = $radius;
}
public function getArea() {
return pi() * $this->radius * $this->radius;
}
}
// 矩形类
class Rectangle extends Shape {
private $width;
private $height;
public function __construct($width, $height) {
$this->width = $width;
$this->height = $height;
}
public function getArea() {
return $this->width * $this->height;
}
}
// 形状计算器
class AreaCalculator {
public function calculateTotalArea(array $shapes) {
$totalArea = 0;
foreach ($shapes as $shape) {
$totalArea += $shape->getArea();
}
return $totalArea;
}
}
// 使用
$shapes = [
new Circle(5),
new Rectangle(10, 20),
new Rectangle(15, 25)
];
$calculator = new AreaCalculator();
$totalArea = $calculator->calculateTotalArea($shapes);
echo "总面积:" . round($totalArea, 2) . "\n";
?>
面向对象的实际应用
简单的用户管理系统
<?php
class User {
private $id;
private $username;
private $email;
private $passwordHash;
private $isActive;
private $createdAt;
private static $users = [];
public function __construct($username, $email, $password) {
$this->id = uniqid();
$this->username = $this->validateUsername($username);
$this->email = $this->validateEmail($email);
$this->passwordHash = $this->hashPassword($password);
$this->isActive = true;
$this->createdAt = date('Y-m-d H:i:s');
self::$users[$this->id] = $this;
}
private function validateUsername($username) {
if (strlen($username) < 3) {
throw new Exception("用户名至少需要3个字符");
}
return $username;
}
private function validateEmail($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new Exception("邮箱格式不正确");
}
return $email;
}
private function hashPassword($password) {
return password_hash($password, PASSWORD_DEFAULT);
}
public function authenticate($password) {
return password_verify($password, $this->passwordHash);
}
public function activate() {
$this->isActive = true;
}
public function deactivate() {
$this->isActive = false;
}
public function isActive() {
return $this->isActive;
}
public function getUsername() {
return $this->username;
}
public function getEmail() {
return $this->email;
}
public function getCreatedAt() {
return $this->createdAt;
}
public function getProfile() {
return [
'username' => $this->username,
'email' => $this->email,
'is_active' => $this->isActive,
'created_at' => $this->createdAt
];
}
public static function getByUsername($username) {
foreach (self::$users as $user) {
if ($user->username === $username) {
return $user;
}
}
return null;
}
public static function getAll() {
return self::$users;
}
public static function getActiveCount() {
$count = 0;
foreach (self::$users as $user) {
if ($user->isActive()) {
$count++;
}
}
return $count;
}
}
// 用户管理服务
class UserService {
public function registerUser($username, $email, $password) {
// 检查用户名是否已存在
$existingUser = User::getByUsername($username);
if ($existingUser) {
throw new Exception("用户名已存在");
}
// 创建新用户
$user = new User($username, $email, $password);
return $user;
}
public function loginUser($username, $password) {
$user = User::getByUsername($username);
if (!$user) {
throw new Exception("用户不存在");
}
if (!$user->authenticate($password)) {
throw new Exception("密码错误");
}
if (!$user->isActive()) {
throw new Exception("账户已被禁用");
}
return $user;
}
public function getUserProfile($username) {
$user = User::getByUsername($username);
if (!$user) {
throw new Exception("用户不存在");
}
return $user->getProfile();
}
public function getAllActiveUsers() {
$activeUsers = [];
foreach (User::getAll() as $user) {
if ($user->isActive()) {
$activeUsers[] = $user->getProfile();
}
}
return $activeUsers;
}
}
// 使用示例
try {
$userService = new UserService();
echo "=== 用户注册 ===\n";
$user1 = $userService->registerUser("alice", "alice@example.com", "password123");
$user2 = $userService->registerUser("bob", "bob@example.com", "password456");
echo "用户注册成功\n";
echo "活跃用户数:" . User::getActiveCount() . "\n\n";
echo "=== 用户登录 ===\n";
$loggedInUser = $userService->loginUser("alice", "password123");
echo "用户 {$loggedInUser->getUsername()} 登录成功\n\n";
echo "=== 获取用户信息 ===\n";
$profile = $userService->getUserProfile("alice");
echo "用户信息:\n";
print_r($profile);
echo "\n=== 活跃用户列表 ===\n";
$activeUsers = $userService->getAllActiveUsers();
foreach ($activeUsers as $user) {
echo "- {$user['username']} ({$user['email']})\n";
}
} catch (Exception $e) {
echo "错误:" . $e->getMessage() . "\n";
}
?>
总结
面向对象编程是一种强大的编程范式,它帮助我们构建更加模块化、可维护和可扩展的应用程序。
关键要点:
-
核心概念:
- 对象是类的实例,包含属性和方法
- 类是对象的模板,定义了结构和行为
- 封装将数据和操作捆绑在一起
- 继承实现代码重用和扩展
- 多态提供灵活的接口
-
OOP的优势:
- 代码更易维护和扩展
- 提高代码重用性
- 增强程序的安全性
- 更好地组织复杂项目
-
设计原则:
- 单一职责原则
- 开闭原则
- 其他SOLID原则(后续章节详细介绍)
最佳实践:
- 将相关的数据和操作封装在同一个类中
- 使用访问控制保护内部数据
- 为类和方法选择有意义的名称
- 保持类的方法简洁和专注
- 优先使用组合而不是继承(在某些情况下)
通过本节学习,你应该对面向对象编程有了基本的理解,为后续学习更高级的OOP特性打下坚实基础。