每日笔记 - 2024.05.16
技术
0
socketInfo() = default;
1 重构
例1
哪种情况应该使用Pull Up Field(字段上移)的重构方法来处理概括关系? 在软件开发中,重构是一种改善代码结构而不改变其外部行为的方法。其中,“字段上移”(Pull Up Field)是将子类中的字段移到其父类的一种重构手段。该方法通常用于处理概括关系,并在以下几种情况中尤其适用:
字段在多个子类中重复存在: 当多个子类中拥有相同的字段时,可以将这个公共字段移到它们的父类中。这不仅减少了代码重复,也增加了维护的便利性和一致性。例如,如果多个子类都有一个名为
address
的字段,这个字段可以上移到父类中。字段在父类中有意义: 如果一个字段在父类的上下文中是有意义的,并且父类的职责应该包含该字段的管理,那么应该将该字段上移到父类中。这样可以更好地体现父类的抽象层次。例如,一个
Employee
父类和Manager
、Engineer
子类,如果salary
字段在这三个类中都有,那么将salary
字段上移到Employee
类中是合理的。统一字段的访问和操作: 当需要统一对某字段的访问和操作逻辑时,将其上移到父类可以简化代码并减少冗余。这样,父类可以提供该字段的统一操作方法,而子类只需继承这些方法。
增强代码的可读性和可维护性: 当某个字段的逻辑和用途在父类中更加清晰,并且可以通过上移字段使代码结构更直观、逻辑更连贯时,使用“字段上移”是有益的。这可以减少代码的复杂性,使得继承层次结构更加明确。
避免重复初始化和管理: 如果多个子类需要对相同的字段进行初始化和管理,将字段上移到父类可以避免重复的初始化代码和管理逻辑,从而简化代码并减少错误的可能性。
示例
假设有以下类结构:
class Manager extends Employee {
private String department;
// other fields and methods
}
class Engineer extends Employee {
private String department;
// other fields and methods
}
在这种情况下,department
字段在Manager
和Engineer
两个子类中重复。通过字段上移,可以将其移到Employee
父类:
class Employee {
protected String department;
// other fields and methods
}
class Manager extends Employee {
// other fields and methods
}
class Engineer extends Employee {
// other fields and methods
}
这样,department
字段被提取到父类Employee
中,减少了代码重复,简化了类结构,并且使得对department
字段的管理更加集中和一致。
总之,使用“字段上移”重构方法可以有效地减少代码重复,提高代码的可维护性和一致性,特别是在处理概括关系时。
例2
重构前的代码:
int low = daysTempRange().getLow();
int high = daysTempRange().getHigh();
withinPlan = plan.withinRange(low, high);
重构后的代码:
withinPlan = plan.withinRange(daysTempRange());
通过对比重构前后的代码,可以看到:
- 原本将
daysTempRange()
的返回值的low
和high
分别提取出来,作为参数传递给withinRange
方法。 - 重构后,直接将
daysTempRange()
的结果(一个对象)传递给withinRange
方法。
这种重构方法直接将整个对象传递给方法,而不是提取对象的部分属性来作为参数传递,这正是 “保持对象完整”(Preserve Whole Object) 的典型案例。
2 C 语言
静态局部变量
虽然静态局部变量在函数调用结束仍然存在,但是其他函数是不能引用它的;静态局部变量存储在静态存储区;若在定义静态局部变量时不赋初值,则编译时自动赋初值0或者空字符;静态局部变量是在编译时赋初值的,在程序运行时它已有初值。
printf
printf()函数的返回值为实际控制输出的字符数。
关系运算
char c;
while ( c = getchar() != EOF)
{
...
}
问进入while循环中语句时c的值是多少?
!=比=的优先级高,c是 getchar() != EOF的结果
多线程
#include <stdatomic.h>
atomic_int account_balance;
void debit(int amount) {
account_balance ‐= amount;
}
void credit(int amount) {
account_balance += amount;
}
#include <threads.h>
static int account_balance;
static mtx_t account_lock;
int debit(int amount) {
if (mtx_lock(&account_lock) == thrd_error) {
return ‐1; /* Indicate error to caller */
}
account_balance ‐= amount;
if (mtx_unlock(&account_lock) == thrd_error) {
return ‐1; /* Indicate error to caller */
}
return 0; /* Indicate success */
}
int credit(int amount) {
if (mtx_lock(&account_lock) == thrd_error) {
return ‐1; /* Indicate error to caller */
}
account_balance += amount; if (mtx_unlock(&account_lock) == thrd_error) {
return ‐1; /* Indicate error to caller */
}
return 0; /* Indicate success */
}
int main(void) {
if(mtx_init(&account_lock, mtx_plain) == thrd_error) {
/* Handle error */
}
/* ... */
}
3 算法
1、在一长度为 N 的有序数列中寻找两个数,使得两数之和等于某指定值。实现该功能最快的算法的平均时间复杂度?
在一个长度为 ( N ) 的有序数列中寻找两个数,使得两数之和等于某指定值,可以采用“双指针”或“夹逼”算法。该算法的平均时间复杂度是 ( O(N) )。
双指针算法
具体步骤如下:
- 初始化两个指针,左指针
left
指向数组的第一个元素,右指针right
指向数组的最后一个元素。 - 计算这两个指针指向元素的和:
- 如果和等于目标值,返回这两个元素。
- 如果和小于目标值,将左指针右移一位(
left++
),以增加总和。 - 如果和大于目标值,将右指针左移一位(
right--
),以减少总和。
- 重复步骤 2,直到找到两个数使它们的和等于目标值,或者左指针超过右指针为止。
代码实现
以下是 Python 实现的示例代码:
def find_two_sum(nums, target):
left, right = 0, len(nums) - 1
while left < right:
current_sum = nums[left] + nums[right]
if current_sum == target:
return (nums[left], nums[right])
elif current_sum < target:
left += 1
else:
right -= 1
return None
# 示例
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
target = 10
result = find_two_sum(nums, target)
print(result) # 输出 (1, 9) 或 (2, 8) 等
算法分析
- 初始化指针:这是一个常数时间操作,复杂度为 ( O(1) )。
- 遍历数组:在最坏情况下,每个元素最多被访问一次(因为每次迭代要么左指针右移,要么右指针左移),所以整个过程最多执行 ( N ) 次操作,复杂度为 ( O(N) )。
因此,双指针算法在有序数列中寻找两个数之和为指定值的平均时间复杂度是 ( O(N) ),这也是该问题的最优时间复杂度。