Search…

Tối Ưu Hóa - Coding Convention

26/09/20207 min read
Hướng dẫn sử dụng và xây dựng Coding Convention.

Coding Convention là chuẩn mực và quy tắc để code, các lập trình viên trong 1 dự án phải tuân theo - tương tự như quy tắc soạn văn bản cần có quy tắc canh lề, quy tắc chữ to nhỏ, đậm nhạt.

Xa hơn nữa, các dự án lớn đòi hỏi đồng bộ giữa các thành viên với nhau là cần thiết, các chuẩn mực sẽ giúp cho các lập trình viên cùng có cách hiểu, cùng nhìn về một hướng, tối ưu thời gian đọc code.

Coding Convention trong bài viết dành cho một số ngôn ngữ họ C như: C, C++, C#, Java, JavaScript.

Các vấn đề khi khai báo biến

Cách đặt tên biến phải mang nghĩa nhất định

Xét ví dụ:

ConvergedSins* ConvergedSins::createConvergedSins(Color a, int b)
{
	ConvergedSins* ret = new ConvergedSins();

	if(ret && ret->init(a, b))
	{
		ret->autorelease();
		return ret;
	}

	SAFE_DEL(ret);
	return NULL;
}

2 biến ab được truyền vào hàm createConvergedSins không mang ý nghĩa. Có thể làm tốt hơn nếu thay a bởi colorb bởi level do trong ngữ cảnh này ab mang 2 ý nghĩa đó.

ConvergedSins* ConvergedSins::createConvergedSins(Color color, int level)
{
	ConvergedSins* ret = new ConvergedSins();

	if(ret && ret->init(color, level))
	{
		ret->autorelease();
		return ret;
	}

	SAFE_DEL(ret);
	return NULL;
}

Sau khi thay đổi đoạn code như trên, khi bảo trì sẽ tránh mất thời gian tra cứu lại ý nghĩa của biến.

Tuy nhiên, không nhất thiết phải lúc nào cũng tuân theo quy tắc, nếu như 1 điều quá hiển nhiên:

int Add(int a, int b)
{
	return a + b;
}

Bảng liệt kê một số loại biến và cách đặt tên

Loại biến Cách đặt tên biến Ví dụ
Biến cục bộ
(local)
Chữ thường toàn bộ cho các ký tự, ngăn cách mỗi từ trong biến bởi dấu gạch dưới. int age_of_person
char* name
Biến toàn cục
(global)
Chữ thường toàn bộ cho các ký tự, tiền tố g_, ngăn cách mỗi từ trong biến bởi gạch dưới. int g_age_of_person
char* g_name
Biến dạng static Chữ thường toàn bộ cho các ký tự, tiền tố s_, ngăn cách mỗi từ trong biến bởi gạch dưới. int s_age_of_person
char* s_name
Biến dạng hằng
(constant)
Viết hoa toàn bộ các ký tự, tiền tố C_ hoặc k_, ngăn cách mỗi từ trong biến bởi gạch dưới. const int C_AGE_OF_PERSON
const char* C_NAME
Các trường
của struct và class
Chữ thường toàn bộ cho các ký tự, tiền tố m_, ngăn cách mỗi từ trong trường này bởi gạch dưới. int m_age_of_person
char* m_name
Các trường static của struct và class Chữ thường toàn bộ cho các ký tự, tiền tố s_, ngăn cách các từ trong trường bởi gạch dưới. int s_age_of_person
char* s_name

Các quy tắc khác

  • Biến truyền vào hàm sẽ viết thường toàn bộ các ký tự, ngăn cách các từ trong biến bởi dấu gạch dưới.
  • Cách khai báo một struct cần viết hoa các ký tự đầu của mỗi từ tên struct và bắt đầu với tiền tố S, ví dụ struct STesla.
  • Cách khai báo một class cần viết hoa các ký tự đầu của mỗi từ trong tên class và bắt đầu với tiền tố C, ví dụ class CMonster.

Các vấn đề khi khai báo hàm

Với Java, bản thân Java Coding Convention rất chặt chẽ, trong các khai báo hàm của Java sử dụng cách viết thường ký tự đầu tiên của tên hàm: createBasicSinsWithData(), playEffectDestroySins().

Với Coding Convention có thể học hỏi từ Java Coding Convention hoặc viết hoa chữ cái đầu tiên của các từ trong tên hàm, ta có ví dụ cho cách thứ 2 này: CreateBasicSinsWithData(), PlayEffectDestroySins().

Cách đặt tên hàm phải mang nghĩa nhất định

Việc đặt tên hàm mang ý nghĩa cũng như đặt tên biến có ý nghĩa như đã trình bày ở trên, đặt tên hàm hay biến càng mang đầy đủ ý nghĩa sẽ càng tốt.

Mức độ 1: xét ví dụ với hàm có ý nghĩa thấp sau, hàm create(...)

LightSins* LightSins::create(eBasicSinsColor color)
{
	LightSins* ret = new LightSins();

	if(ret && ret->init(color))
	{
		ret->autorelease();
		return ret;
	}

	SAFE_DEL(ret);
	return NULL;
}

Mức độ 2: gia tăng ý nghĩa hơn cho hàm create(...), đặt lại tên hàm với nhiều từ hơn, sửa create(...) thành createLightSins(...)

LightSins* LightSins::createLightSins(eBasicSinsColor color)
{
	LightSins* ret = new LightSins();

	if(ret && ret->init(color))
	{
		ret->autorelease();
		return ret;
	}

	SAFE_DEL(ret);
	return NULL;
}

Mức độ 3: làm rõ nghĩa hơn nữa, điều chỉnh thành createLightSinsWithColor(...)

LightSins* LightSins::createLightSinsWithColor(eBasicSinsColor color)
{
	LightSins* ret = new LightSins();

	if(ret && ret->init(color))
	{
		ret->autorelease();
		return ret;
	}

	SAFE_DEL(ret);
	return NULL;
}

Khi sử dụng hàm trên sẽ tránh mất thời gian hơn trong bảo trì xem cần truyền tham số thuộc loại nào vào hàm, đôi lúc không cần xem phần hiện thực cũng có thể đoán được ý nghĩa của hàm.

Các vấn đề khác về soạn thảo văn bản

Level của 1 khối code

Việc đặt level đúng của 1 khối code là rất quan trọng, giúp xác định nhanh hơn "tầm vực" của 1 dòng code và xác định vấn đề nhanh hơn.

Xét ví dụ sau, code không được trình bày đúng level

if(m_fAdd1ScoreTimer == 0.0f)
{
	if(m_iScorePool > 0)
	{
	int addScore = (int)(m_iScorePool * 0.2f);
	if(addScore < 1) 
	addScore = 1;

m_iScorePool -= addScore;
m_iCurrentScore += addScore;
m_fAdd1ScoreTimer = DELAY_ADD_1_SCORE;

		if(m_iScorePool == 0)
		{
m_pScoreLabel->runAction(
CCSequence::create(
CCScaleTo::create(0.05f, 2.0f*m_ScaleFactorScore),
CCScaleTo::create(0.1f, 1.0f*m_ScaleFactorScore), NULL
));
	}
	}
}

Với code như trên, có thể nhầm lẫn m_iCurrentScore += addScore; nằm cùng cấp với dòng if(m_fAdd1ScoreTimer == 0.0f), tức là nó không nằm trong if.

Thay vì vậy, đưa nó vào đúng level của nó:

if(m_fAdd1ScoreTimer == 0.0f)
{
	if(m_iScorePool > 0)
	{
		int addScore = (int)(m_iScorePool * 0.2f);
		if(addScore < 1) 
			addScore = 1;

		m_iScorePool -= addScore;
		m_iCurrentScore += addScore;
		m_fAdd1ScoreTimer = DELAY_ADD_1_SCORE;

		if(m_iScorePool == 0)
		{
			m_pScoreLabel->runAction(CCSequence::create(
				CCScaleTo::create(0.05f, 2.0f*m_scaleFactorPts),
				CCScaleTo::create(0.1f, 1.0f*m_scaleFactorPts),
				NULL
			));
		}
	}
}

Đặt dấu (chấm, phẩy, hai chấm, chấm phẩy, hỏi, chấm than)

Theo phương pháp đặt dấu trong văn bản thì "dấu câu" trong code cũng có vài nét tương đồng. Nguyên tắc là dấu sẽ nằm ngay phía sau ký tự cuối và sau đó là đến khoảng trắng rồi mới đến ký tự kế tiếp.

Ví dụ đặt dấu sai, xem xét khảo sát tại dấu phẩy , và dấu chấm .

  • Twinkle ,twinkle ,little star ,how I wonder what you are. (sai chỗ dấu phẩy, đúng chỗ dấu chấm)
  • Twinkle,twinkle,little star,how I wonder what you are . (sai chỗ dấu phẩy, sai chỗ dấu chấm)
  • Twinkle, twinkle, little star, how I wonder what you are . (đúng chỗ dấu phẩy, sai chỗ dấu chấm)

Cách đặt dấu đúng dựa theo nguyên tắc đã đề cập phía trên:

Twinkle, twinkle, little star, how I wonder what you are.

Từ cách trình bày văn bản như trên, tận dụng vào việc trình bày code, xét ví dụ code về việc đặt dấu sau

playActionFollowDelete(1,this,callfunc_selector(SinsDestroy::playDone))  ;

Các dấu phẩy hay chấm phẩy như trên đặt không tốt, có thể trình bày lại như sau

playActionFollowDelete(1, this, callfunc_selector(SinsDestroy::playDone));

Cách dòng trong code

Khảo sát đoạn code sau, đây là đoạn code đã cách dòng tốt

void CLoadingData::releaseAllResource()
{
	m_iNumBackgroundAudios = 0;

	// RELEASE SPRITE
	for(u8 i = 0; i < m_tSpritePacks.size(); i++)
	{
		GetSpriteManager()->unloadSprite(m_tSpritePacks.at(i));
	}
	m_tSpritePacks.clear();
	
	// RELEASE AFX
	for(u8 i = 0; i < m_tAudioPacks.size(); i++)
	{
		unloadEffect(m_tAudioPacks.at(i));			
	}
	m_tAudioPacks.clear();
}

Xem xét đoạn từ comment // RELEASE SPRITE và tới đoạn // RELEASE AFX, giữa 2 đoạn đó cách nhau bởi 1 dòng trống (blank) (dòng 11). Ý nghĩa của nó nhằm ngăn cách 2 khối code có 2 chức năng khác nhau, như vậy khi bảo trì có thể nhanh chóng xác định được đoạn code nào làm việc gì thay vì để liên tục từ trên xuống như đoạn code bên dưới.

void CLoadingData::releaseAllResource()
{
	m_iNumBackgroundAudios = 0;
	// RELEASE SPRITE
	for(u8 i = 0; i < m_tSpritePacks.size(); i++)
	{
		GetSpriteManager()->unloadSprite(m_tSpritePacks.at(i));
	}
	m_tSpritePacks.clear();
	// RELEASE AFX
	for(u8 i = 0; i < m_tAudioPacks.size(); i++)
	{
		unloadEffect(m_tAudioPacks.at(i));			
	}
	m_tAudioPacks.clear();
}

Từ đây, bạn có thể tự thiết kế nên chuẩn mực chung của nhóm làm việc, hoặc học cách tuân theo chuẩn mực cho các dự án sau này.

Bài chung series

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024