自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一篇聊透好重構(gòu)與壞重構(gòu)

開(kāi)發(fā) 項(xiàng)目管理
重構(gòu)是軟件開(kāi)發(fā)的必要組成部分,但在進(jìn)行重構(gòu)時(shí)需要深思熟慮,并尊重現(xiàn)有的代碼庫(kù)和團(tuán)隊(duì)動(dòng)態(tài)。重構(gòu)的目的是在不改變代碼外部行為的情況下改進(jìn)代碼的內(nèi)部結(jié)構(gòu)。

這些年來(lái),我雇傭過(guò)很多開(kāi)發(fā)人員。他們當(dāng)中有很多人都堅(jiān)信我們的代碼需要大量重構(gòu)。但問(wèn)題是:幾乎在每一個(gè)案例中,其他開(kāi)發(fā)人員都發(fā)現(xiàn)他們新重構(gòu)的代碼更難理解和維護(hù)。此外,代碼的運(yùn)行速度通常也更慢,漏洞也更多。

別誤會(huì)我的意思。重構(gòu)本質(zhì)上并不是壞事。它是保持代碼庫(kù)健康的關(guān)鍵部分。問(wèn)題是,糟糕的重構(gòu)就是糟糕。而且,在試圖讓事情變得更好的同時(shí),我們很容易掉入讓事情變得更糟的陷阱。

因此,讓我們來(lái)看看什么是好的重構(gòu),什么是壞的重構(gòu),以及如何避免成為大家都害怕在代碼庫(kù)附近看到的那個(gè)開(kāi)發(fā)人員。

重構(gòu)的好與壞

抽象可以是好的。抽象可以是壞的。關(guān)鍵是要知道何時(shí)以及如何應(yīng)用抽象。讓我們來(lái)看看一些常見(jiàn)的陷阱以及如何避免它們。

1. 大幅改變編碼風(fēng)格

我見(jiàn)過(guò)的最常見(jiàn)的錯(cuò)誤之一,就是開(kāi)發(fā)人員在重構(gòu)過(guò)程中完全改變編碼風(fēng)格。這種情況通常發(fā)生在來(lái)自不同背景的人或?qū)μ囟ň幊谭妒接袕?qiáng)烈意見(jiàn)的人身上。

讓我們來(lái)看一個(gè)例子。假設(shè)我們有一段代碼需要清理:

重構(gòu)之前:

// ?? this code could be cleaner
function processUsers(users: User[]) {
  const result = [];
  for (let i = 0; i < users.length; i++) {
    if (users[i].age >= 18) {
      const formattedUser = {
        name: users[i].name.toUpperCase(),
        age: users[i].age,
        isAdult: true
      };
      result.push(formattedUser);
    }
  }
  return result;
}

壞的重構(gòu):

import * as R from 'ramda';

// ?? adopted a completely different style + library
const processUsers = R.pipe(
  R.filter(R.propSatisfies(R.gte(R.__, 18), 'age')),
  R.map(R.applySpec({
    name: R.pipe(R.prop('name'), R.toUpper),
    age: R.prop('age'),
    isAdult: R.always(true)
  }))
);

雖然這個(gè)重構(gòu)版本可能會(huì)吸引函數(shù)式編程愛(ài)好者,但它引入了一個(gè)新庫(kù)(Ramda)和一種完全不同的編碼風(fēng)格。對(duì)于不熟悉這種方法的團(tuán)隊(duì)來(lái)說(shuō),維護(hù)起來(lái)可能是一場(chǎng)噩夢(mèng)。

好的重構(gòu):

// ? cleaner and more conventional 
function processUsers(users: User[]): FormattedUser[] {
  return users
    .filter(user => user.age >= 18)
    .map(user => ({
      name: user.name.toUpperCase(),
      age: user.age,
      isAdult: true
    }));
}

該版本改進(jìn)了原始代碼,使用了 filter 和 map 等更多慣用的 JavaScript 方法,更加簡(jiǎn)潔易讀,但并未引入全新的范式或外部依賴(lài)關(guān)系。

2.不必要的抽象

我曾經(jīng)雇傭過(guò)一個(gè)人,他在不了解底層代碼的情況下添加了大量新的抽象概念。他們開(kāi)始將不應(yīng)該分組的東西分組,并且隨著時(shí)間的推移(故意)產(chǎn)生分歧。他們合并了一些不該合并的配置(不同的 API 需要不同的配置)。

重構(gòu)之前:

// ?? this code could be cleaner
function processUsers(users: User[]) {
  const result = [];
  for (let i = 0; i < users.length; i++) {
    if (users[i].age >= 18) {
      const formattedUser = {
        name: users[i].name.toUpperCase(),
        age: users[i].age,
        isAdult: true
      };
      result.push(formattedUser);
    }
  }
  return result;
}

壞的重構(gòu):

// ?? there are way more layers and abstractions here than necessary
class UserProcessor {
  private users: User[];

  constructor(users: User[]) {
    this.users = users;
  }

  public process(): FormattedUser[] {
    return this.filterAdults().formatUsers();
  }

  private filterAdults(): UserProcessor {
    this.users = this.users.filter(user => user.age >= 18);
    return this;
  }

  private formatUsers(): FormattedUser[] {
    return this.users.map(user => ({
      name: this.formatName(user.name),
      age: user.age,
      isAdult: true
    }));
  }

  private formatName(name: string): string {
    return name.toUpperCase();
  }
}

const processUsers = (users: User[]): FormattedUser[] => {
  return new UserProcessor(users).process();
};

這種重構(gòu)引入了一個(gè)具有多個(gè)方法的類(lèi),看起來(lái)似乎更 "面向?qū)ο?,但實(shí)際上更復(fù)雜,也更難一目了然。

好的重構(gòu):

// ? cleaner and more conventional 
const isAdult = (user: User): boolean => user.age >= 18;

const formatUser = (user: User): FormattedUser => ({
  name: user.name.toUpperCase(),
  age: user.age,
  isAdult: true
});

function processUsers(users: User[]): FormattedUser[] {
  return users.filter(isAdult).map(formatUser);
}

該版本將邏輯分解為可重復(fù)使用的小函數(shù),而不會(huì)引入不必要的復(fù)雜性。

3.增加不一致性

我曾見(jiàn)過(guò)這樣的情況:開(kāi)發(fā)人員更新代碼庫(kù)的一部分,使其工作方式與其他部分完全不同,試圖使其 "更好"。這往往會(huì)給其他開(kāi)發(fā)人員帶來(lái)困惑和挫敗感,因?yàn)樗麄儾坏貌辉诓煌L(fēng)格之間進(jìn)行上下文切換。

假設(shè)我們有一個(gè) React 應(yīng)用程序,在該應(yīng)用程序中,我們始終使用 React Query 來(lái)獲取數(shù)據(jù):

// Throughout the app
import { useQuery } from 'react-query';

function UserProfile({ userId }) {
  const { data: user, isLoading } = useQuery(['user', userId], fetchUser);

  if (isLoading) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

現(xiàn)在,想象一下開(kāi)發(fā)人員決定只在一個(gè)組件中使用 Redux 工具包:

// One-off component
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPosts } from './postsSlice';

function PostList() {
  const dispatch = useDispatch();
  const { posts, status } = useSelector((state) => state.posts);

  useEffect(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  if (status === 'loading') return <div>Loading...</div>;
  return <div>{posts.map(post => <div key={post.id}>{post.title}</div>)}</div>;
}

這種不一致性令人沮喪,因?yàn)樗鼉H僅為一個(gè)組件引入了完全不同的狀態(tài)管理模式。

更好的方法是堅(jiān)持使用 React Query:

// Consistent approach
import { useQuery } from 'react-query';

function PostList() {
  const { data: posts, isLoading } = useQuery('posts', fetchPosts);

  if (isLoading) return <div>Loading...</div>;
  return <div>{posts.map(post => <div key={post.id}>{post.title}</div>)}</div>;
}

該版本保持了一致性,使用 React Query 在整個(gè)應(yīng)用程序中獲取數(shù)據(jù)。它更簡(jiǎn)單,不需要其他開(kāi)發(fā)人員只為一個(gè)組件學(xué)習(xí)新的模式。

請(qǐng)記住,代碼庫(kù)的一致性非常重要。如果你需要引入一種新模式,請(qǐng)首先考慮如何獲得團(tuán)隊(duì)的認(rèn)同,而不是制造一次性的不一致。

4.重構(gòu)前不了解代碼

我所見(jiàn)過(guò)的最大問(wèn)題之一就是在學(xué)習(xí)代碼的過(guò)程中,為了學(xué)習(xí)而重構(gòu)代碼。這是一個(gè)糟糕的想法。我曾看到過(guò)這樣的評(píng)論:你應(yīng)該用 6-9 個(gè)月的時(shí)間來(lái)處理一段特定的代碼。否則,你很可能會(huì)產(chǎn)生錯(cuò)誤、影響性能等。

重構(gòu)之前:

// ?? a bit too much hard coded stuff here
function fetchUserData(userId: string) {
  const cachedData = localStorage.getItem(`user_${userId}`);
  if (cachedData) {
    return JSON.parse(cachedData);
  }

  return api.fetchUser(userId).then(userData => {
    localStorage.setItem(`user_${userId}`, JSON.stringify(userData));
    return userData;
  });
}

壞的重構(gòu):

// ?? where did the caching go?
function fetchUserData(userId: string) {
  return api.fetchUser(userId);
}

重構(gòu)者可能會(huì)認(rèn)為他們?cè)诤?jiǎn)化代碼,但實(shí)際上他們已經(jīng)刪除了一個(gè)重要的緩存機(jī)制,而該機(jī)制是為了減少 API 調(diào)用和提高性能而設(shè)置的。

好的重構(gòu):

// ? cleaner code preserving the existing behavior
async function fetchUserData(userId: string) {
  const cachedData = await cacheManager.get(`user_${userId}`);
  if (cachedData) {
    return cachedData;
  }

  const userData = await api.fetchUser(userId);
  await cacheManager.set(`user_${userId}`, userData, { expiresIn: '1h' });
  return userData;
}

此次重構(gòu)在保持緩存行為的同時(shí),還可能通過(guò)使用更復(fù)雜的過(guò)期緩存管理器來(lái)改進(jìn)緩存行為。

5.了解業(yè)務(wù)背景

我曾經(jīng)加入過(guò)一家公司,它背負(fù)著可怕的傳統(tǒng)代碼包袱。我領(lǐng)導(dǎo)了一個(gè)項(xiàng)目,將一家電子商務(wù)公司遷移到一個(gè)新的、現(xiàn)代的、更快的、更好的技術(shù)...... angular.js

事實(shí)證明,這項(xiàng)業(yè)務(wù)在很大程度上依賴(lài)于搜索引擎優(yōu)化,而我們構(gòu)建了一個(gè)緩慢而臃腫的單頁(yè)面應(yīng)用程序。

兩年來(lái),我們除了提供一個(gè)速度更慢、漏洞更多、可維護(hù)性更差的網(wǎng)站復(fù)制品外,什么也沒(méi)提供。為什么會(huì)這樣?領(lǐng)導(dǎo)這個(gè)項(xiàng)目的人(我--我是這個(gè)場(chǎng)景中的混蛋)以前從未在這個(gè)網(wǎng)站上工作過(guò)。我當(dāng)時(shí)又年輕又笨。

讓我們來(lái)看一個(gè)現(xiàn)代錯(cuò)誤的例子:

壞的重構(gòu):

// ?? a single page app for an SEO-focused site is a bad idea
function App() {
  return (
    <Router>
      <Switch>
        <Route path="/product/:id" component={ProductDetails} />
      </Switch>
    </Router>
  );
}

這種方法看似現(xiàn)代簡(jiǎn)潔,但完全是客戶(hù)端渲染。對(duì)于嚴(yán)重依賴(lài)搜索引擎優(yōu)化的電子商務(wù)網(wǎng)站來(lái)說(shuō),這可能是災(zāi)難性的。

好的重構(gòu):

// ? server render an SEO-focused site
export const getStaticProps: GetStaticProps = async () => {
  const products = await getProducts();
  return { props: { products } };
};

export default function ProductList({ products }) {
  return (
    <div>
      ...
    </div>
  );
}

這種基于 Next.js 的方法提供開(kāi)箱即用的服務(wù)器端渲染,這對(duì)搜索引擎優(yōu)化至關(guān)重要。它還能提供更好的用戶(hù)體驗(yàn),加快初始頁(yè)面加載速度,并為連接速度較慢的用戶(hù)提高性能。Remix 也同樣適用于這一目的,在服務(wù)器端渲染和搜索引擎優(yōu)化方面具有類(lèi)似的優(yōu)勢(shì)。

6.過(guò)度合并代碼

我曾經(jīng)雇過(guò)一個(gè)人,他第一天在我們的后臺(tái)工作,就立即開(kāi)始重構(gòu)代碼。我們有很多 Firebase 函數(shù),有些函數(shù)的設(shè)置與其他函數(shù)不同,比如超時(shí)和內(nèi)存分配。

這是我們最初的設(shè)置。

重構(gòu)之前:

// ?? we had this same code 40+ times in the codebase, we could perhaps consolidate
export const quickFunction = functions
  .runWith({ timeoutSeconds: 60, memory: '256MB' })
  .https.onRequest(...);

export const longRunningFunction = functions
  .runWith({ timeoutSeconds: 540, memory: '1GB' })
  .https.onRequest(...);

這個(gè)人決定將所有這些函數(shù)封裝在一個(gè) createApi 函數(shù)中。

壞的重構(gòu):

// ?? blindly consolidating settings that should not be
const createApi = (handler: RequestHandler) => {
  return functions
    .runWith({ timeoutSeconds: 300, memory: '512MB' })
    .https.onRequest((req, res) => handler(req, res));
};

export const quickFunction = createApi(handleQuickRequest);
export const longRunningFunction = createApi(handleLongRunningRequest);

這次重構(gòu)將所有 API 設(shè)置為相同的設(shè)置,而無(wú)法覆蓋每個(gè) API。這是個(gè)問(wèn)題,因?yàn)橛袝r(shí)我們需要對(duì)不同的函數(shù)進(jìn)行不同的設(shè)置。

更好的方法是允許 Firebase 選項(xiàng)通過(guò)每個(gè)應(yīng)用程序接口傳遞。

好的重構(gòu):

// ? setting good defaults, but letting anyone override
const createApi = (handler: RequestHandler, options: ApiOptions = {}) => {
  return functions
    .runWith({ timeoutSeconds: 300, memory: '512MB', ...options })
    .https.onRequest((req, res) => handler(req, res));
};

export const quickFunction = createApi(handleQuickRequest, { timeoutSeconds: 60, memory: '256MB' });
export const longRunningFunction = createApi(handleLongRunningRequest, { timeoutSeconds: 540, memory: '1GB' });

這樣,我們既能保持抽象的優(yōu)勢(shì),又能保留我們所需的靈活性。在合并或抽象時(shí),請(qǐng)始終考慮你所服務(wù)的用例。不要為了 "更簡(jiǎn)潔" 的代碼而犧牲靈活性。確保你的抽象實(shí)現(xiàn)了原始實(shí)現(xiàn)所提供的全部功能。

說(shuō)真的,在開(kāi)始 "改進(jìn)" 代碼之前,請(qǐng)先了解代碼。我們?cè)谙乱淮尾渴鹨恍?yīng)用程序接口時(shí)就遇到了問(wèn)題,如果不進(jìn)行盲目的重構(gòu),這些問(wèn)題是可以避免的。

如何正確重構(gòu)

值得注意的是,你確實(shí)需要重構(gòu)代碼。但要正確對(duì)待。我們的代碼并不完美,我們的代碼需要清理,但要與代碼庫(kù)保持一致,熟悉代碼,對(duì)抽象要精挑細(xì)選。

下面是一些成功重構(gòu)的技巧:

  1. Be incremental: Make small, manageable changes rather than sweeping rewrites.循序漸進(jìn):進(jìn)行小規(guī)模、可控的修改,而不是大刀闊斧的改寫(xiě)。
  2. Deeply understand code before doing significant refactors or new abstractions.在進(jìn)行重大重構(gòu)或新抽象之前,深入理解代碼。
  3. Match the existing code style: Consistency is key for maintainability.與現(xiàn)有代碼風(fēng)格相匹配:一致性是可維護(hù)性的關(guān)鍵。
  4. Avoid too many new abstractions: Keep it simple unless complexity is truly warranted.避免過(guò)多的新抽象概念:保持簡(jiǎn)單,除非確實(shí)需要復(fù)雜。
  5. Avoid adding new libraries, especially of a very different programming style, without buy-in from the team.避免在未獲得團(tuán)隊(duì)認(rèn)可的情況下添加新的庫(kù),尤其是編程風(fēng)格迥異的庫(kù)。
  6. Write tests before refactoring and update them as you go. This ensures you're maintaining the original functionality.在重構(gòu)前編寫(xiě)測(cè)試,并在重構(gòu)過(guò)程中更新測(cè)試。這能確保你保持原有的功能。
  7. Hold your coworkers accountable to these principles.讓你的同事對(duì)這些原則負(fù)責(zé)。

圖片圖片

更好地重構(gòu)的工具和技術(shù)

為了確保你的重構(gòu)是有益而非有害的,請(qǐng)考慮使用以下技術(shù)和工具:

規(guī)范工具

使用規(guī)范工具來(lái)執(zhí)行一致的代碼風(fēng)格并捕捉潛在的問(wèn)題。Prettier 可以幫助自動(dòng)格式化為一致的風(fēng)格,而 Eslint 則可以幫助進(jìn)行更細(xì)致的一致性檢查,你可以用自己的插件輕松定制。

代碼審查

在合并重構(gòu)代碼之前,實(shí)施全面的代碼審查,以獲得同行的反饋意見(jiàn)。這有助于及早發(fā)現(xiàn)潛在問(wèn)題,并確保重構(gòu)代碼符合團(tuán)隊(duì)標(biāo)準(zhǔn)和期望。

測(cè)試

編寫(xiě)并運(yùn)行測(cè)試,確保重構(gòu)代碼不會(huì)破壞現(xiàn)有功能。Vitest[1] 是一款快速、可靠、易用的測(cè)試運(yùn)行程序,默認(rèn)情況下無(wú)需任何配置。對(duì)于可視化測(cè)試,可以考慮使用 Storybook[2]。React Testing Library[3] 是一套用于測(cè)試 React 組件的實(shí)用工具(還有 Angular 和更多變體)。

人工智能工具

讓人工智能來(lái)幫助你進(jìn)行重構(gòu),至少是那些能夠與你現(xiàn)有的編碼風(fēng)格和習(xí)慣相匹配的重構(gòu)。

結(jié)論

重構(gòu)是軟件開(kāi)發(fā)的必要組成部分,但在進(jìn)行重構(gòu)時(shí)需要深思熟慮,并尊重現(xiàn)有的代碼庫(kù)和團(tuán)隊(duì)動(dòng)態(tài)。重構(gòu)的目的是在不改變代碼外部行為的情況下改進(jìn)代碼的內(nèi)部結(jié)構(gòu)。

請(qǐng)記住,最好的重構(gòu)往往是最終用戶(hù)看不到的,但卻能大大方便開(kāi)發(fā)人員的工作。它們能提高可讀性、可維護(hù)性和效率,而不會(huì)破壞整個(gè)統(tǒng)。

下一次,當(dāng)你有為一段代碼制定 "大計(jì)劃" 的沖動(dòng)時(shí),請(qǐng)退后一步。徹底了解它,考慮更改帶來(lái)的影響,然后逐步改進(jìn),你的團(tuán)隊(duì)一定會(huì)感謝你的。

本文譯自:https://www.builder.io/blog/good-vs-bad-refactoring

Reference

[1] Vitest: https://vitest.dev/

[2] Storybook: https://storybook.js.org/

[3] React Testing Library: https://github.com/testing-library/react-testing-library

責(zé)任編輯:武曉燕 來(lái)源: 前端F2E
相關(guān)推薦

2021-07-02 08:51:28

Vite線程項(xiàng)目

2022-08-16 09:05:39

Kubernetes權(quán)限管理

2024-09-05 10:17:34

2021-12-29 07:18:20

重構(gòu)工具資源

2024-08-12 11:22:10

2024-05-09 09:41:45

2024-12-11 18:24:29

2012-12-17 12:58:18

WebjQuery重構(gòu)

2022-01-28 08:47:25

軟件系統(tǒng)重構(gòu)

2022-08-15 07:34:36

vite項(xiàng)目Vue3

2013-07-09 10:44:47

2024-07-08 12:40:18

MySQL索引失效

2012-05-15 01:16:19

開(kāi)發(fā)重構(gòu)Java

2012-05-04 09:54:23

Linux服務(wù)器

2018-07-10 10:00:15

Android架構(gòu)MVC

2018-08-24 21:25:02

編程語(yǔ)言代碼重構(gòu)GitHub

2024-02-26 00:00:00

架構(gòu)老化重構(gòu)

2018-07-17 15:11:27

Android重構(gòu)插件化

2021-08-03 08:13:48

重構(gòu)API代碼

2011-06-07 16:47:28

Android 重構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)