首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
142 阅读
2
php接口优化 使用curl_multi_init批量请求
132 阅读
3
2024年备考系统架构设计师
102 阅读
4
《从菜鸟到大师之路 ElasticSearch 篇》
102 阅读
5
PHP 文件I/O
89 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
785
篇文章
累计收到
8
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
785
篇与
的结果
2023-12-13
什么是面向切片编程?
面向切片(Aspect-oriented Programming,AOP)是一种软件开发的方法论,它旨在通过将横切关注点从主要业务逻辑中分离出来,提供更高层次的模块化和可维护性。它通过将跨越多个对象、类或组件的共同功能(称为横切关注点)从核心业务逻辑中提取出来,并以模块化方式将其封装起来。面向切片编程可以用于解决以下几种常见问题:日志记录:通过在关键方法的前后添加日志记录的代码,可以实现统一的日志记录功能,而无需修改每个方法。事务管理:通过将事务管理代码从业务逻辑中分离出来,可以实现更加灵活和可重用的事务处理机制。安全性检查:通过在关键方法的前后添加安全性检查的代码,可以确保只有具有特定权限的用户才能访问敏感信息。性能监控:通过在关键方法的前后添加性能监控代码,可以实现对系统性能的实时监测和统计。下面以日志记录为例进行详细说明。假设我们有一个银行账户管理系统,其中涉及到以下两个核心方法:public void deposit(double amount) { // 存款操作 } public void withdraw(double amount) { // 取款操作 }为了实现日志记录功能,我们可以创建一个切面(Aspect),并在切面中定义一个通知(Advice)。通知是在特定的连接点(Join Point)上执行的代码片段,例如方法调用或方法执行的前后。public aspect LoggingAspect { before(): execution(public void deposit(double)) { System.out.println("记录存款操作日志"); } before(): execution(public void withdraw(double)) { System.out.println("记录取款操作日志"); } }上述代码中,LoggingAspect 是切面类,before() 是前置通知。它使用 execution(public void deposit(double)) 来指定连接点为 deposit() 方法的执行前,同样地, execution(public void withdraw(double)) 指定连接点为 withdraw() 方法的执行前。当程序运行时,切面中的通知代码会在指定的连接点之前执行,从而实现了在关键方法的执行前添加日志记录的功能。总结来说,面向切片编程通过将横切关注点从主要业务逻辑中分离出来,提供了一种方法将共性功能模块化,并在需要时以声明式方式将其应用到不同的连接点上。这种方式能够提高代码的可维护性、可重用性和灵活性,使得系统更易于扩展和修改。
2023年12月13日
11 阅读
0 评论
0 点赞
2023-12-13
如何使用ChatGPT写代码
如何使用ChatGPT写代码须知事项ChatGPT可以纠正错误、简化复杂的想法和解决漏洞,让现有的代码更加简洁。开发者想要节省时间的话,可以用ChatGPT给应用程序创建脚手架、模板和样板代码。ChatGPT会出错,所以无法取代软件工程师。一定要先测试ChatGPT写出来的代码,确认无误才能投入使用。方法1使用ChatGPT写代码。1给代码创建结构。ChatGPT在软件开发中最大的用途之一就是给程序创建脚手架。把你想要编写的程序类型告诉ChatGPT,然后粘贴任何库、依赖项、文件名和其它要包含的详细信息。你可以自然地使用完整的句子和段落输入请求,ChatGPT会在几秒钟内生成一个代码模板。2生成代码片段。请求ChatGPT协助编写函数、例程和其它代码,这样就不需要花太多时间在Stack Overflow网站上查找语法示例。比如说,如果你请求ChatGPT“编写一个利用切片反转字符串的Python函数”,ChatGPT不仅会生成代码,还会解释它的工作原理。你之后可以在整个项目中使用这些代码作为样板文件。3补充现有的代码。询问ChatGPT如何更改现有的代码,让它运行得更快、更流畅,或是做其它任务。把你的代码粘贴到对话框,然后阐明你想要做的更改。比如说,“根据ID列按数字顺序显示结果”,或是“把这些项目加到3号位置的列表中”。4简化和重构复杂的代码。给ChatGPT发送代码,请求它“简化这段代码”,让你的程序更紧凑。想要在不改变原始结构的情况下重新编写代码,可以说:“重构这段代码,提高它的效率。”ChatGPT会清理代码,让它变得更简洁,还会总结它所做的更改,作为你日后的参考。5解释代码片段。不管你对某个代码片段还是整个程序感到困惑,都可以要求ChatGPT解惑。把代码粘贴到ChatGPT中,然后询问:“这个函数是干什么用的?”,或是“这个算法是如何运作的?”6找出错误和漏洞。ChatGPT可以帮助识别和修复代码中的错误。把代码粘贴到字段中,然后询问:“这段代码有什么问题?”,或是“要怎样修复这个程序中的错误?”。必要时,ChatGPT会请求你输入更多信息,然后提供修复方法。7生成另一套代码。即使你的程序顺利运作,或许还有更好的编码方式。把你的代码发给ChatGPT,询问:“有没有更好的方法来完成<你的目标>?”ChatGPT会提出建议,比如建议替代的算法,然后详细解释它的所有建议。8将代码翻译成其它编程语言。如果你已经掌握了一种编程语言,可以使用ChatGPT将代码转换成你不太熟悉的语言。举个例子,将一个C++程序粘贴到ChatGPT中,请求它“把代码翻译成Java”。9在几秒钟内测试函数。与其自己花时间给函数编写测试用例,不如让ChatGPT帮你。ChatGPT会对你指定的函数执行几项测试用例,并报告结果。[1]10给人类编写文档和注释。不必自己给程序编写操作文档或注释,只需要把代码粘贴到ChatGPT中,请求它“解释这段代码”就行了。之后可以将ChatGPT的解释添加到程序的注释中,甚至把它放进官方文档里。方法2让Chat GPT写出有效的代码。1给ChatGPT提供充足的信息来编写你需要的代码。ChatGPT依靠你提供的上下文和它本身的训练数据生成响应。详细说明自己的要求,ChatGPT才能生成有用的代码。清楚说明你想让程序或网站执行的任务,想使用的编程语言和程序的用途。举个例子,如果你想让ChatGPT给你的宠物看护业务建立一个网站,让潜在客户查看你提供的服务,并咨询预订情况,可以这样写:[2]“建立一个简单的单页网站宣传我的宠物看护业务,包括遛狗和寄宿服务。创建一个表来罗列各项服务和价格,其中寄宿服务每晚60块,遛狗单次30分钟20块。创建一个联系表单让请求服务的客户填写。表单必须允许客户从日历中选择他们想要的日期。把客户的回复发送到我的电子邮件地址me@myemailaddress.com。”在这个示例中,ChatGPT会提供创建一个简单网站的HTML和CSS代码,你之后可以把它上传到网络主机。这段代码现在是你的了,你可以随意做一些细微的调整来个性化网站。2补充更多细节,以获取想要的代码。ChatGPT是会话式AI,它可以从当前会话中引用多达3000个单词,所以你有足够的空间来补充编码请求。[3]我们之前要求了ChatGPT编写一个宠物看护业务的网站。如果你还想在页面上的表格添加宠物美容价格,只需要说“把单次100块的美容服务添加到收费表上”,ChatGPT就会重新生成代码来包含你要求做的更改。你也可以添加其它细节,比如“把标题标签改成“上海平价宠物看护服务”,或是“在客户提交表格后显示一个感谢页面”。3通过提问来澄清错误或消除困惑。如果AI聊天机器人编写的代码令人困惑或完全错误,一定要告诉它。请求ChatGPT进行澄清,它会根据训练数据检查自己的工作,并对代码做出相应的调整。你也可以提供更详细的上下文,以获取更准确的答案。[4]记住,ChatGPT不是软件工程师,它无法理解细微差别和训练数据中没有的上下文信息。它主要依赖2021年前的互联网可用数据,无法随时根据用户需求在网络上搜索最新的内容。[5]4不要全盘相信ChatGPT写的代码,一定要亲自做测试。虽然ChatGPT作为一个AI聊天机器人可以写出好得令人难以置信的代码,但是它经常会犯错,而且不会或拒绝纠正。如果ChatGPT的训练数据含有错误的代码示例,它会把这些错误的示例当成事实。所以,ChatGPT在自己编写程序方面还不够可靠。你可以把它编写的代码作为参考,但是它不能取代软件工程师,你还是得自己学习编程。小提示ChatGPT的训练数据截至2021年,如果你正在开发的程序需要使用最新的库或编码约定,ChatGPT将无法提供准确的代码。你跟ChatGPT的对话可能被用于改进服务质量,切记不要泄露任何隐私。[6]滑到页面底部 --> 点击右下角 --> ChatGPT 传送门。
2023年12月13日
34 阅读
0 评论
0 点赞
2023-12-11
如何编写编写干净的 PHP 代码
如何编写编写干净的 PHP 代码介绍软件工程原理,摘自 Robert C. Martin 的著作 Clean Code, 适用于PHP。这不是风格指南。这是一份生产指南 PHP 中可读、可重用和可重构的软件。并非这里的每一项原则都必须严格遵守,甚至更少是普遍遵守的 商定。这些是指导方针,仅此而已,但它们是许多准则的编纂 _Clean Code_ 作者多年的集体经验。灵感来自 clean-code-javascript。尽管许多开发人员仍在使用 PHP 5,但本文中的大多数示例仅适用于 PHP 7.1+。变量使用有意义且可发音的变量名称坏:$ymdstr = $moment->format('y-m-d');好:$currentDate = $moment->format('y-m-d');对相同类型的变量使用相同的词汇坏:getUserInfo(); getUserData(); getUserRecord(); getUserProfile();好:getUser();使用可搜索的名称(第 1 部分)我们将阅读比我们编写的更多的代码。重要的是,我们编写的代码是 可读和可搜索。通过_不_命名最终有意义的变量 了解我们的程序,我们伤害了我们的读者。 使你的名字可搜索。坏:// What the heck is 448 for? $result = $serializer->serialize($data, 448);好:$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);使用可搜索的名称(第 2 部分)坏:class User { // What the heck is 7 for? public $access = 7; } // What the heck is 4 for? if ($user->access & 4) { // ... } // What's going on here? $user->access ^= 2;好:class User { public const ACCESS_READ = 1; public const ACCESS_CREATE = 2; public const ACCESS_UPDATE = 4; public const ACCESS_DELETE = 8; // User as default can read, create and update something public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE; } if ($user->access & User::ACCESS_UPDATE) { // do edit ... } // Deny access rights to create something $user->access ^= User::ACCESS_CREATE;使用解释变量坏:$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches[1], $matches[2]);不错:它更好,但我们仍然严重依赖正则表达式。$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); [, $city, $zipCode] = $matches; saveCityZipCode($city, $zipCode);好:通过命名子模式来减少对正则表达式的依赖。$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches['city'], $matches['zipCode']);避免嵌套太深,提早返回(第 1 部分)过多的 if-else 语句会使代码难以理解。显式更好 比隐含。坏:function isShopOpen($day): bool { if ($day) { if (is_string($day)) { $day = strtolower($day); if ($day === 'friday') { return true; } elseif ($day === 'saturday') { return true; } elseif ($day === 'sunday') { return true; } return false; } return false; } return false; }好:function isShopOpen(string $day): bool { if (empty($day)) { return false; } $openingDays = ['friday', 'saturday', 'sunday']; return in_array(strtolower($day), $openingDays, true); }避免嵌套太深,提早返回(第 2 部分)坏:function fibonacci(int $n) { if ($n < 50) { if ($n !== 0) { if ($n !== 1) { return fibonacci($n - 1) + fibonacci($n - 2); } return 1; } return 0; } return 'Not supported'; }好:function fibonacci(int $n): int { if ($n === 0 || $n === 1) { return $n; } if ($n >= 50) { throw new Exception('Not supported'); } return fibonacci($n - 1) + fibonacci($n - 2); }避免思维导图不要强迫代码的读者翻译变量的含义。 显式比隐式好。坏:$l = ['Austin', 'New York', 'San Francisco']; for ($i = 0; $i < count($l); $i++) { $li = $l[$i]; doStuff(); doSomeOtherStuff(); // ... // ... // ... // Wait, what is `$li` for again? dispatch($li); }好:$locations = ['Austin', 'New York', 'San Francisco']; foreach ($locations as $location) { doStuff(); doSomeOtherStuff(); // ... // ... // ... dispatch($location); }不要添加不需要的上下文如果你的类/对象名称告诉你一些事情,不要在你的 变量名称。坏:class Car { public $carMake; public $carModel; public $carColor; //... }好:class Car { public $make; public $model; public $color; //... }比较使用相同的比较不好:简单的比较会将字符串转换为整数。$a = '42'; $b = 42; if ($a != $b) { // The expression will always pass }比较回来了,但实际上是! 字符串与整数不同。$a != $b`FALSETRUE42`42好:相同的比较将比较类型和值。$a = '42'; $b = 42; if ($a !== $b) { // The expression is verified }比较返回 。$a !== $b`TRUE`Null 合并运算符Null 合并是 PHP 7 中引入的一个新运算符。已将 null 合并运算符添加为句法糖,用于需要将三元与 结合使用的常见情况。如果它存在并且不存在,则返回其第一个操作数;否则,它将返回其第二个操作数。??`isset()`null坏:if (isset($_GET['name'])) { $name = $_GET['name']; } elseif (isset($_POST['name'])) { $name = $_POST['name']; } else { $name = 'nobody'; }好:$name = $_GET['name'] ?? $_POST['name'] ?? 'nobody';功能使用默认参数而不是短路或条件不好:这不好,因为可以.$breweryName`NULL`function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void { // ... }不错:这种观点比以前的版本更容易理解,但它更好地控制了变量的值。function createMicrobrewery($name = null): void { $breweryName = $name ?: 'Hipster Brew Co.'; // ... }好:您可以使用类型提示,并确保 不会是 .$breweryName`NULL`function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void { // ... }函数参数(理想情况下为 2 个或更少)限制函数参数的数量非常重要,因为它使 更轻松地测试您的函数。拥有三个以上会导致组合爆炸 你必须用每个单独的参数测试大量不同的情况。零参数是理想的情况。一两个论点是可以的,应该避免三个论点。 除此之外,任何事情都应该被合并。通常,如果您有两个以上的 参数,那么你的函数试图做太多。如果不是,大多数 在更高级别的对象作为参数就足够了。坏:class Questionnaire { public function __construct( string $firstname, string $lastname, string $patronymic, string $region, string $district, string $city, string $phone, string $email ) { // ... } }好:class Name { private $firstname; private $lastname; private $patronymic; public function __construct(string $firstname, string $lastname, string $patronymic) { $this->firstname = $firstname; $this->lastname = $lastname; $this->patronymic = $patronymic; } // getters ... } class City { private $region; private $district; private $city; public function __construct(string $region, string $district, string $city) { $this->region = $region; $this->district = $district; $this->city = $city; } // getters ... } class Contact { private $phone; private $email; public function __construct(string $phone, string $email) { $this->phone = $phone; $this->email = $email; } // getters ... } class Questionnaire { public function __construct(Name $name, City $city, Contact $contact) { // ... } }函数名称应说明它们的作用坏:class Email { //... public function handle(): void { mail($this->to, $this->subject, $this->body); } } $message = new Email(...); // What is this? A handle for the message? Are we writing to a file now? $message->handle();好:class Email { //... public function send(): void { mail($this->to, $this->subject, $this->body); } } $message = new Email(...); // Clear and obvious $message->send();函数应该只是一个抽象级别当你有多个抽象级别时,你的函数通常是 做得太多了。拆分功能可提高可重用性,更轻松 测试。坏:function parseBetterPHPAlternative(string $code): void { $regexes = [ // ... ]; $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { // ... } } $ast = []; foreach ($tokens as $token) { // lex... } foreach ($ast as $node) { // parse... } }也不好:我们已经执行了一些功能,但功能仍然非常复杂且无法测试。parseBetterPHPAlternative()function tokenize(string $code): array { $regexes = [ // ... ]; $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { $tokens[] = /* ... */; } } return $tokens; } function lexer(array $tokens): array { $ast = []; foreach ($tokens as $token) { $ast[] = /* ... */; } return $ast; } function parseBetterPHPAlternative(string $code): void { $tokens = tokenize($code); $ast = lexer($tokens); foreach ($ast as $node) { // parse... } }好:最好的解决方案是移出函数的依赖关系。parseBetterPHPAlternative()class Tokenizer { public function tokenize(string $code): array { $regexes = [ // ... ]; $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { $tokens[] = /* ... */; } } return $tokens; } } class Lexer { public function lexify(array $tokens): array { $ast = []; foreach ($tokens as $token) { $ast[] = /* ... */; } return $ast; } } class BetterPHPAlternative { private $tokenizer; private $lexer; public function __construct(Tokenizer $tokenizer, Lexer $lexer) { $this->tokenizer = $tokenizer; $this->lexer = $lexer; } public function parse(string $code): void { $tokens = $this->tokenizer->tokenize($code); $ast = $this->lexer->lexify($tokens); foreach ($ast as $node) { // parse... } } }不要使用标志作为函数参数标志告诉用户此函数执行多项操作。函数应 做一件事。如果函数遵循不同的代码路径,请拆分函数 基于布尔值。坏:function createFile(string $name, bool $temp = false): void { if ($temp) { touch('./temp/' . $name); } else { touch($name); } }好:function createFile(string $name): void { touch($name); } function createTempFile(string $name): void { touch('./temp/' . $name); }避免副作用如果函数执行除 和 中的值之外的任何操作,则会产生副作用 返回另一个或多个值。副作用可能是写入文件、修改 一些全局变量,或者不小心把你所有的钱都汇给一个陌生人。现在,您确实需要偶尔在程序中出现副作用。和以前一样 例如,您可能需要写入文件。您要做的是集中在哪里 你正在这样做。不要有多个函数和类写入特定的 文件。有一个服务可以做到这一点。一个也是唯一一个。重点是避免常见的陷阱,例如在没有 任何结构,使用可变数据类型,可以由任何内容写入,而不是 集中出现副作用的位置。如果你能做到这一点,你会更快乐 比绝大多数其他程序员都要好。坏:// Global variable referenced by following function. // If we had another function that used this name, now it'd be an array and it could break it. $name = 'Ryan McDermott'; function splitIntoFirstAndLastName(): void { global $name; $name = explode(' ', $name); } splitIntoFirstAndLastName(); var_dump($name); // ['Ryan', 'McDermott'];好:function splitIntoFirstAndLastName(string $name): array { return explode(' ', $name); } $name = 'Ryan McDermott'; $newName = splitIntoFirstAndLastName($name); var_dump($name); // 'Ryan McDermott'; var_dump($newName); // ['Ryan', 'McDermott'];不要写入全局函数在许多语言中,污染全局变量是一种不好的做法,因为您可能会与另一种语言发生冲突 库和 API 的用户将不明智,直到他们在 生产。让我们考虑一个例子:如果你想拥有配置数组怎么办? 你可以像 一样编写全局函数,但它可能会与另一个库发生冲突 试图做同样的事情。config()坏:function config(): array { return [ 'foo' => 'bar', ]; }好:class Configuration { private $configuration = []; public function __construct(array $configuration) { $this->configuration = $configuration; } public function get(string $key): ?string { // null coalescing operator return $this->configuration[$key] ?? null; } }加载配置并创建类的实例Configuration$configuration = new Configuration([ 'foo' => 'bar', ]);现在,您必须在应用程序中使用实例。Configuration不要使用单一实例模式Singleton 是一种反模式。转述自布莱恩·巴顿(Brian Button):它们通常被用作全局实例,为什么这么糟糕?因为您将应用程序的依赖项隐藏在代码中,而不是通过接口公开它们。制作全球性的东西以避免传递它是一种代码味道。他们违反了单一责任原则:因为他们控制着自己的创造和生命周期。它们固有地导致代码紧密耦合。这使得在许多情况下在测试中伪造它们变得相当困难。它们在应用程序的生存期内携带状态。测试的另一个打击,因为你最终可能会遇到需要订购测试的情况,这对单元测试来说是一个很大的否定。为什么?因为每个单元测试都应该彼此独立。Misko Hevery对问题的根源也有很好的想法。坏:class DBConnection { private static $instance; private function __construct(string $dsn) { // ... } public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } // ... } $singleton = DBConnection::getInstance();好:class DBConnection { public function __construct(string $dsn) { // ... } // ... }创建类的实例并使用 DSN 对其进行配置。DBConnection$connection = new DBConnection($dsn);现在,您必须在应用程序中使用实例。DBConnection封装条件语句坏:if ($article->state === 'published') { // ... }好:if ($article->isPublished()) { // ... }避免负面条件坏:function isDOMNodeNotPresent(DOMNode $node): bool { // ... } if (! isDOMNodeNotPresent($node)) { // ... }好:function isDOMNodePresent(DOMNode $node): bool { // ... } if (isDOMNodePresent($node)) { // ... }避免使用条件这似乎是一项不可能完成的任务。第一次听到这句话时,大多数人会说, “没有声明,我怎么能做任何事情?”答案是 在许多情况下,您可以使用多态性来实现相同的任务。第二个 问题通常是,“嗯,这很好,但我为什么要这样做?这 答案是我们之前学到的一个干净的代码概念:一个函数应该只做 一件事。当您具有具有语句的类和函数时,您 告诉用户您的函数执行了不止一件事。记得 只做一件事。if`if`坏:class Airplane { // ... public function getCruisingAltitude(): int { switch ($this->type) { case '777': return $this->getMaxAltitude() - $this->getPassengerCount(); case 'Air Force One': return $this->getMaxAltitude(); case 'Cessna': return $this->getMaxAltitude() - $this->getFuelExpenditure(); } } }好:interface Airplane { // ... public function getCruisingAltitude(): int; } class Boeing777 implements Airplane { // ... public function getCruisingAltitude(): int { return $this->getMaxAltitude() - $this->getPassengerCount(); } } class AirForceOne implements Airplane { // ... public function getCruisingAltitude(): int { return $this->getMaxAltitude(); } } class Cessna implements Airplane { // ... public function getCruisingAltitude(): int { return $this->getMaxAltitude() - $this->getFuelExpenditure(); } }避免类型检查(第 1 部分)PHP 是非类型化的,这意味着您的函数可以接受任何类型的参数。 有时你会被这种自由所咬,它变得很诱人 函数中的类型检查。有很多方法可以避免这样做。 首先要考虑的是一致的 API。坏:function travelToTexas($vehicle): void { if ($vehicle instanceof Bicycle) { $vehicle->pedalTo(new Location('texas')); } elseif ($vehicle instanceof Car) { $vehicle->driveTo(new Location('texas')); } }好:function travelToTexas(Vehicle $vehicle): void { $vehicle->travelTo(new Location('texas')); }避免类型检查(第 2 部分)如果您正在使用字符串、整数和数组等基本基元值, 你使用 PHP 7+,你不能使用多态性,但你仍然觉得有必要 类型检查,您应该考虑类型声明或严格模式。它为您提供了基于标准PHP语法的静态类型。 手动类型检查的问题在于,这样做需要很多 额外的措辞是,你得到的虚假“类型安全”并不能弥补损失 可读性。保持你的PHP干净,编写好的测试,并有良好的代码审查。 否则,除了PHP严格类型声明或严格模式之外,还可以执行所有这些操作。坏:function combine($val1, $val2): int { if (! is_numeric($val1) || ! is_numeric($val2)) { throw new Exception('Must be of type Number'); } return $val1 + $val2; }好:function combine(int $val1, int $val2): int { return $val1 + $val2; }删除死代码死代码和重复代码一样糟糕。没有理由保留它 您的代码库。如果它没有被调用,请摆脱它!它仍然是安全的 如果您仍然需要它,请在您的版本历史记录中。坏:function oldRequestModule(string $url): void { // ... } function newRequestModule(string $url): void { // ... } $request = newRequestModule($requestUrl); inventoryTracker('apples', $request, 'www.inventory-awesome.io');好:function requestModule(string $url): void { // ... } $request = requestModule($requestUrl); inventoryTracker('apples', $request, 'www.inventory-awesome.io');对象和数据结构使用对象封装在 PHP 中,您可以为方法设置 、 和关键字。 使用它,您可以控制对象的属性修改。public`protected`private当您想在获取对象属性之外执行更多操作时,您没有 查找和更改代码库中的每个访问器。使在执行 .set封装内部表示形式。在获取和设置时易于添加日志记录和错误处理。继承此类后,可以重写默认功能。您可以延迟加载对象的属性,例如从 服务器。此外,这是开/闭原则的一部分。坏:class BankAccount { public $balance = 1000; } $bankAccount = new BankAccount(); // Buy shoes... $bankAccount->balance -= 100;好:class BankAccount { private $balance; public function __construct(int $balance = 1000) { $this->balance = $balance; } public function withdraw(int $amount): void { if ($amount > $this->balance) { throw new \Exception('Amount greater than available balance.'); } $this->balance -= $amount; } public function deposit(int $amount): void { $this->balance += $amount; } public function getBalance(): int { return $this->balance; } } $bankAccount = new BankAccount(); // Buy shoes... $bankAccount->withdraw($shoesPrice); // Get balance $balance = $bankAccount->getBalance();使对象具有私有/受保护的成员public方法和属性对于更改来说是最危险的,因为某些外部代码可能很容易依赖它们,并且您无法控制哪些代码依赖于它们。类中的修改对类的所有用户都是危险的。protected修饰符和 public 一样危险,因为它们在任何子类的范围内都可用。这实际上意味着公共和受保护之间的区别仅在于访问机制,但封装保证保持不变。类中的修改对于所有后代类都是危险的。privatemodifier 保证代码仅在单个类的边界内修改是危险的(修改是安全的,并且不会产生 Jenga 效应)。因此,在默认情况下以及需要为外部类提供访问权限时使用。private`public/protected`有关更多信息,您可以阅读 Fabien Potencier 撰写的有关此主题的博客文章。坏:class Employee { public $name; public function __construct(string $name) { $this->name = $name; } } $employee = new Employee('John Doe'); // Employee name: John Doe echo 'Employee name: ' . $employee->name;好:class Employee { private $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $employee = new Employee('John Doe'); // Employee name: John Doe echo 'Employee name: ' . $employee->getName();类首选组合而不是继承正如四人帮在《设计模式》中所说的那样, 在可能的情况下,您应该更喜欢组合而不是继承。有很多 使用继承的充分理由和使用组合的充分理由。 这句格言的要点是,如果你的思想本能地去做 继承,试着想想组合是否可以更好地模拟你的问题。在一些 案例可以。那么,您可能想知道,“我什么时候应该使用继承?它 取决于你手头的问题,但这是一个不错的继承清单 比构图更有意义:你的继承代表一种“是”的关系,而不是“有”的关系 关系(人-动物与用户->>UserDetails)。您可以重用基类中的代码(人类可以像所有动物一样移动)。您希望通过更改基类来对派生类进行全局更改。 (改变所有动物移动时的热量消耗)。坏:class Employee { private $name; private $email; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } // ... } // Bad because Employees "have" tax data. // EmployeeTaxData is not a type of Employee class EmployeeTaxData extends Employee { private $ssn; private $salary; public function __construct(string $name, string $email, string $ssn, string $salary) { parent::__construct($name, $email); $this->ssn = $ssn; $this->salary = $salary; } // ... }好:class EmployeeTaxData { private $ssn; private $salary; public function __construct(string $ssn, string $salary) { $this->ssn = $ssn; $this->salary = $salary; } // ... } class Employee { private $name; private $email; private $taxData; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } public function setTaxData(EmployeeTaxData $taxData): void { $this->taxData = $taxData; } // ... }避免使用流畅的界面Fluent 接口是一个对象 面向 API,旨在通过使用方法链接提高源代码的可读性。虽然可能有一些上下文,通常是构建器对象,其中 pattern 降低了代码的冗长程度(例如 PHPUnit Mock Builder 或 Doctrine Query Builder), 更多时候,它需要付出一些代价:中断封装。中断装饰器。在测试套件中更难嘲笑。使提交的差异更难阅读。有关更多信息,您可以阅读 Marco Picetta 撰写的有关此主题的完整博客文章。坏:class Car { private $make = 'Honda'; private $model = 'Accord'; private $color = 'white'; public function setMake(string $make): self { $this->make = $make; // NOTE: Returning this for chaining return $this; } public function setModel(string $model): self { $this->model = $model; // NOTE: Returning this for chaining return $this; } public function setColor(string $color): self { $this->color = $color; // NOTE: Returning this for chaining return $this; } public function dump(): void { var_dump($this->make, $this->model, $this->color); } } $car = (new Car()) ->setColor('pink') ->setMake('Ford') ->setModel('F-150') ->dump();好:class Car { private $make = 'Honda'; private $model = 'Accord'; private $color = 'white'; public function setMake(string $make): void { $this->make = $make; } public function setModel(string $model): void { $this->model = $model; } public function setColor(string $color): void { $this->color = $color; } public function dump(): void { var_dump($this->make, $this->model, $this->color); } } $car = new Car(); $car->setColor('pink'); $car->setMake('Ford'); $car->setModel('F-150'); $car->dump();首选期末课程应尽可能使用关键字:final它可以防止不受控制的继承链。它鼓励作文。它鼓励单一责任原则。它鼓励开发人员使用您的公共方法,而不是扩展类来访问受保护的方法。它允许您更改代码,而不会破坏使用您的类的应用程序。唯一的条件是你的类应该实现一个接口,并且没有定义其他公共方法。有关更多信息,您可以阅读 Marco Pivetta (Ocramius) 撰写的有关此主题的博客文章。坏:final class Car { private $color; public function __construct($color) { $this->color = $color; } /** * @return string The color of the vehicle */ public function getColor() { return $this->color; } }好:interface Vehicle { /** * @return string The color of the vehicle */ public function getColor(); } final class Car implements Vehicle { private $color; public function __construct($color) { $this->color = $color; } public function getColor() { return $this->color; } }固体SOLID 是 Michael Feathers 为罗伯特·马丁 (Robert Martin) 命名的前五个原则引入的首字母缩写词,意思是面向对象编程和设计的五个基本原则。S:单一责任原则 (SRP)O:开/闭原理 (OCP)L:李氏替代原理(LSP)I:接口隔离原则(ISP)D:依赖反转原则 (DIP)单一责任原则 (SRP)正如 Clean Code 中所述,“一个类不应该有多个原因 改变”。将一个具有许多功能的类塞进包装是很诱人的,例如 当您在航班上只能携带一个行李箱时。这样做的问题是 你的班级在概念上不会有凝聚力,它会给出很多理由 来改变。尽量减少更改类所需的次数非常重要。 这很重要,因为如果一个类中有太多功能,并且您修改了其中的一部分, 可能很难理解这将如何影响其他依赖模块 您的代码库。坏:class UserSettings { private $user; public function __construct(User $user) { $this->user = $user; } public function changeSettings(array $settings): void { if ($this->verifyCredentials()) { // ... } } private function verifyCredentials(): bool { // ... } }好:class UserAuth { private $user; public function __construct(User $user) { $this->user = $user; } public function verifyCredentials(): bool { // ... } } class UserSettings { private $user; private $auth; public function __construct(User $user) { $this->user = $user; $this->auth = new UserAuth($user); } public function changeSettings(array $settings): void { if ($this->auth->verifyCredentials()) { // ... } } }开/闭原理 (OCP)正如 Bertrand Meyer 所说,“软件实体(类、模块、函数、 等)应该开放以进行扩展,但关闭以进行修改。那有什么作用 但意思是?这个原则基本上表明你应该允许用户 在不更改现有代码的情况下添加新功能。坏:abstract class Adapter { protected $name; public function getName(): string { return $this->name; } } class AjaxAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'ajaxAdapter'; } } class NodeAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'nodeAdapter'; } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { $adapterName = $this->adapter->getName(); if ($adapterName === 'ajaxAdapter') { return $this->makeAjaxCall($url); } elseif ($adapterName === 'httpNodeAdapter') { return $this->makeHttpCall($url); } } private function makeAjaxCall(string $url): Promise { // request and return promise } private function makeHttpCall(string $url): Promise { // request and return promise } }好:interface Adapter { public function request(string $url): Promise; } class AjaxAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class NodeAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { return $this->adapter->request($url); } }Liskov 替代原理 (LSP)对于一个非常简单的概念来说,这是一个可怕的术语。它的正式定义是“如果 S 是 T 的子类型,则 T 类型的对象可以替换为 S 类型的对象 (即,S 类型的对象可以替换 T 类型的对象),而不改变任何 该程序的理想属性(正确性、执行的任务、 等等)。这是一个更可怕的定义。对此最好的解释是,如果你有一个父类和一个子类, 那么基类和子类可以互换使用,而不会得到 结果不正确。这可能仍然令人困惑,所以让我们来看看 经典的 Square-Rectangle 示例。在数学上,正方形是一个矩形,但是 如果通过继承使用“is-a”关系对其进行建模,则很快 惹上麻烦了。坏:class Rectangle { protected $width = 0; protected $height = 0; public function setWidth(int $width): void { $this->width = $width; } public function setHeight(int $height): void { $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square extends Rectangle { public function setWidth(int $width): void { $this->width = $this->height = $width; } public function setHeight(int $height): void { $this->width = $this->height = $height; } } function printArea(Rectangle $rectangle): void { $rectangle->setWidth(4); $rectangle->setHeight(5); // BAD: Will return 25 for Square. Should be 20. echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()) . PHP_EOL; } $rectangles = [new Rectangle(), new Square()]; foreach ($rectangles as $rectangle) { printArea($rectangle); }好:最好的方法是将四边形分开,并为两种形状分配一个更通用的子类型。尽管正方形和矩形表面上相似,但它们是不同的。 正方形与菱形有很多共同之处,矩形与平行四边形有很多共同之处,但它们不是亚型。 正方形、矩形、菱形和平行四边形是独立的形状,具有各自的属性,尽管相似。interface Shape { public function getArea(): int; } class Rectangle implements Shape { private $width = 0; private $height = 0; public function __construct(int $width, int $height) { $this->width = $width; $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square implements Shape { private $length = 0; public function __construct(int $length) { $this->length = $length; } public function getArea(): int { return $this->length ** 2; } } function printArea(Shape $shape): void { echo sprintf('%s has area %d.', get_class($shape), $shape->getArea()).PHP_EOL; } $shapes = [new Rectangle(4, 5), new Square(5)]; foreach ($shapes as $shape) { printArea($shape); }接口隔离原则 (ISP)ISP 指出,“不应强迫客户端依赖于 他们不使用。一个很好的例子可以证明这个原则是 需要大型设置对象的类。不需要客户端设置 大量的选择是有益的,因为大多数时候他们不需要 所有设置。使它们成为可选有助于防止出现“胖接口”。坏:interface Employee { public function work(): void; public function eat(): void; } class HumanEmployee implements Employee { public function work(): void { // ....working } public function eat(): void { // ...... eating in lunch break } } class RobotEmployee implements Employee { public function work(): void { //.... working much more } public function eat(): void { //.... robot can't eat, but it must implement this method } }好:不是每个工人都是雇员,但每个雇员都是工人。interface Workable { public function work(): void; } interface Feedable { public function eat(): void; } interface Employee extends Feedable, Workable { } class HumanEmployee implements Employee { public function work(): void { // ....working } public function eat(): void { //.... eating in lunch break } } // robot can only work class RobotEmployee implements Workable { public function work(): void { // ....working } }依赖关系反转原则 (DIP)该原则规定了两件基本的事情:高级模块不应依赖于低级模块。两者都应该 依赖于抽象。抽象不应依赖于细节。细节应取决于 抽象。一开始可能很难理解,但如果你使用过PHP框架(如Symfony),你就会看到这个原则以依赖的形式实现 注射 (DI)。虽然它们不是相同的概念,但 DIP 保持了高层次 模块,了解其低级模块的详细信息并设置它们。 它可以通过 DI 来实现这一点。这样做的一个巨大好处是它减少了 模块之间的耦合。耦合是一种非常糟糕的开发模式,因为 它使你的代码难以重构。坏:class Employee { public function work(): void { // ....working } } class Robot extends Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }好:interface Employee { public function work(): void; } class Human implements Employee { public function work(): void { // ....working } } class Robot implements Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }不要重复自己 (DRY)尽量遵守 DRY 原则。尽最大努力避免重复代码。重复代码很糟糕,因为 这意味着如果需要,有不止一个地方可以更改某些内容 改变一些逻辑。想象一下,如果你经营一家餐馆,你跟踪你的库存:所有的 西红柿、洋葱、大蒜、香料等。如果您有多个列表 你保持这个状态,那么当你端上一道菜时,一切都必须更新 西红柿在里面。如果您只有一个列表,则只有一个位置可以更新!通常,您有重复的代码,因为您有两个或更多代码 不同的东西,有很多共同点,但它们的差异迫使你 具有两个或多个单独的函数,它们执行许多相同的操作。删除 重复代码意味着创建一个抽象,可以处理这组不同的 只有一个函数/模块/类的东西。获得正确的抽象是至关重要的,这就是为什么你应该遵循 “类”部分中列出的 SOLID 原则。糟糕的抽象可能是 比重复代码更糟糕,所以要小心!话虽如此,如果你能做到 一个好的抽象,去做吧!不要重复自己,否则你会发现自己 随时更新多个位置,想要更改一件事。坏:function showDeveloperList(array $developers): void { foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); $experience = $developer->getExperience(); $githubLink = $developer->getGithubLink(); $data = [$expectedSalary, $experience, $githubLink]; render($data); } } function showManagerList(array $managers): void { foreach ($managers as $manager) { $expectedSalary = $manager->calculateExpectedSalary(); $experience = $manager->getExperience(); $githubLink = $manager->getGithubLink(); $data = [$expectedSalary, $experience, $githubLink]; render($data); } }好:function showList(array $employees): void { foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); $experience = $employee->getExperience(); $githubLink = $employee->getGithubLink(); $data = [$expectedSalary, $experience, $githubLink]; render($data); } }非常好:最好使用代码的紧凑版本。function showList(array $employees): void { foreach ($employees as $employee) { render([$employee->calculateExpectedSalary(), $employee->getExperience(), $employee->getGithubLink()]); } }
2023年12月11日
9 阅读
0 评论
0 点赞
2023-12-09
引入JavaScript文件的4种方式
在HTML文件中,可以使用以下4种方式引入JavaScript文件:1.外部文件方式(External file):将JavaScript代码保存为一个独立的文件,并通过src属性链接到HTML文件中。 示例:<script src="script.js"></script>2.内部文件方式(Internal file):在HTML文件中使用<script>标签并将JavaScript代码放在其中,但不指定外部文件路径。 示例:<script type="text/javascript"> // JavaScript代码 </script>3.动态加载方式(Dynamic loading):使用JavaScript的createElement和appendChild方法来动态创建和插入<script>标签。 示例:var script = document.createElement('script'); script.src = 'script.js'; document.body.appendChild(script);4.按需加载方式(Lazy loading):使用defer或async属性来控制脚本的加载和执行时机。defer属性表示脚本将在文档解析完毕后执行,同时保持脚本的异步加载,多个延迟脚本按顺序执行。示例:<script src="script.js" defer></script>async属性表示脚本将在下载完成后立即执行,不会阻塞页面的解析和渲染。示例:<script src="script.js" async></script>请注意,以上引入方式可以单独使用,也可以组合使用,根据实际需求选择合适的方式。
2023年12月09日
14 阅读
0 评论
0 点赞
2023-12-07
js对象操作
JavaScript 和 HTML DOM 参考手册 js的变量类型有字符串,布尔等 在操作这些变量类型的时候,可以将他们看成是对象来操作 因为js 把一切都封装成对象来看 获取字符串的长度var str = 'hello world'; console.log(str.length); //11 console.log(str.substr(0,5)); // hello console.log(str); // hello world; var arr = \['a','b','c','d'\]; console.log(arr.join('~'));//将一个数组拆分为字符串 console.log(str.split(' '));// 将一个字符串分割为数组 String 字符串对象length属性: 长度indexOf(string) 返回出现字符串的位置substr(num1,[num2]) 截取字符串replace(str1,str2) 字符串替换var str = 'abcdef'; str.indexOf('d') //3 str.substr(0,3) //'abc' str.replace('a','f') //'fbcdef'Date 日期对象getFullYear() 返回年份(4位)getMonth() 返回月份 0-11getDate() 返回日期 1-31getHours() 返回小时数 0-23getMinutes() 返回分钟数 0-59getSeconds() 返回秒数 0-59var d = new Date(); d.getFullYear()+'-'+d.getMonth()+'-'+d.getDate()+' '+d.getHours()+':'+d.getMinutes()+':'+d.getSeconds() //日期YmdHisMath 数学对象ceil(数值) 大于或等于该数的最小整数,即小数 做四舍五入 取整floor(数值) 小于或等于该数的最大整数 即小数 不做四舍五入 取整var num = 3.56; Math.ceil(num) //4 Math.floor(num) //3
2023年12月07日
8 阅读
0 评论
0 点赞
1
...
50
51
52
...
157