Tôi nên tổ chức cây nguồn của mình như thế nào?


89

Tôi là một nhà phát triển cá nhân làm việc, phần lớn, trên các dự án web (W / LAMP) và đôi khi, trên các dự án C / C ++ (không phải GUI) ở quy mô trung bình.

Tôi thường đấu tranh với cấu trúc cây mã nguồn của mình. Trên thực tế, thông thường, tôi không hoàn thành một dự án mà không đổ toàn bộ cây và sắp xếp lại các mảnh ba bốn lần, điều này thực sự tốn rất nhiều nỗ lực và hơn nữa, kết quả cuối cùng có vẻ như là một sự thỏa hiệp.

Đôi khi, tôi kết thúc với việc phân loại quá mức nguồn - cây rất dài của các thư mục và thư mục con. Vào những lúc khác, tôi chỉ đơn giản là kết thúc việc tập trung tất cả các tệp trong một thư mục cụ thể dựa trên mục đích lớn hơn mà chúng phục vụ và do đó dẫn đến các thư mục 'hỗn loạn' trong nguồn.

Tôi muốn hỏi:

  • Có bất kỳ nguyên tắc / logic / thực tiễn tốt nhất nào có thể giúp tôi tốt hơn trong việc cấu trúc cây nguồn của mình không?
  • Có bất kỳ kỹ thuật đồ họa / sơ đồ nào (ví dụ: DFD trong trường hợp dataflow) có thể giúp tôi hình dung cây nguồn của mình trước dựa trên phân tích dự án không?
  • Chiến lược nào để áp dụng để cấu trúc cây đa phương tiện - cây liên quan đến dự án?

Về tiền thưởng : Tôi đánh giá cao câu trả lời hiện có với các thành viên chia sẻ thực tiễn của riêng họ, tuy nhiên, tôi muốn khuyến khích các câu trả lời chung chung và mang tính hướng dẫn (hoặc tài nguyên) và nhiều phản hồi hơn từ các thành viên.


8
Tôi không có thời gian cho một bài luận ngay bây giờ, nhưng "đặt tên cho những gì chúng là", "đặt những thứ chúng thuộc về", "giữ những thứ tương tự gần nhau", và cuối cùng, "đừng lo lắng về nó , bạn hy vọng có một IDE sẽ giúp bạn nhanh chóng điều hướng giữa các đoạn mã ".
John Saunders

@ John, tôi không giỏi lắm với IDE (s), tôi thường rút ra một Notepad ++ hoặc vi tùy thuộc vào HĐH. Điều đó làm cho mọi thứ khó khăn hơn một chút. Phần còn lại của các điểm là hữu ích nhưng một lần nữa, nó tập trung vào việc đưa ra các quyết định khó khăn như các hàm log (nhật ký lỗi, v.v.) gần hơn với logic ứng dụng hoặc DAL hoặc quản lý bộ đệm hoặc trình quản lý xem. Lỗi có khả năng xảy ra gần như bằng nhau trên bất kỳ lỗi nào.
kiểm tra123

3
Có lẽ một khi bạn đã đến lúc có loại câu hỏi này, đã đến lúc để một số công cụ thực hiện một số công việc cho bạn. Và ghi nhật ký rõ ràng là một mối quan tâm đa chức năng, được sử dụng bởi tất cả các phần của ứng dụng (nếu bạn đang sử dụng loại mã cần ghi nhật ký). Một câu nói nhỏ khác là "đặt mã lên trên mã sử dụng nó", vì vậy việc ghi nhật ký phải ở gần đầu, có thể trong \ tiện ích.
John Saunders

@ John: Được đánh giá cao. Có thể tôi nên bắt đầu tìm kiếm một IDE. Nhật thực có vẻ đầy hứa hẹn.
kiểm tra123

1
@ check123 "... sắp xếp lại các mảnh ghép ba bốn lần ..." Cách thực hành phổ biến: Đổi câu hỏi quản lý, do đó, không phải là có nên xây dựng một hệ thống thí điểm và vứt nó đi không. Bạn sẽ làm điều đó. Câu hỏi duy nhất là liệu lên kế hoạch trước để xây dựng một throwaway, hoặc hứa hẹn để cung cấp các throwaway cho khách hàng “- Frederick P. Brooks Jr., The Mythical Man-Month:. Các bài viết về Công nghệ phần mềm
shawnhcorey

Câu trả lời:


25

Bố cục cây nguồn nên phản ánh kiến ​​trúc; như một hệ quả tất yếu, một kiến ​​trúc có cấu trúc tốt có thể dẫn đến bố cục cây nguồn có cấu trúc tốt. Tôi khuyên bạn nên đọc mô hình Lớp POSA1 , cố gắng khớp kiến ​​trúc của bạn vào cấu trúc lớp, sau đó đặt tên cho từng lớp kết quả và sử dụng làm cơ sở cho phân cấp nguồn của bạn. Lấy kiến trúc ba tầng chung làm cơ sở:

  • thuyết trình / webService (trình bày giao diện dịch vụ web theo logic kinh doanh của chúng tôi)
  • logic / * (mô-đun logic nghiệp vụ đi vào đây)
  • Storage / sql (API lưu trữ phía sau tại đây - giao diện này sử dụng giao diện SQL để lưu trữ vào cơ sở dữ liệu)
  • hồn / * (mã tiện ích - có thể sử dụng bởi tất cả các lớp khác, nhưng không tham chiếu bên ngoài sử dụng, hãy vào đây)

Lưu ý rằng các lớp không chứa mã trực tiếp, mà được sử dụng nghiêm ngặt để tổ chức các mô-đun.

Trong một mô-đun, tôi sử dụng cách bố trí sau:

  • <module> (đường dẫn đến mô-đun trực tiếp; xác định giao diện mô-đun)
  • <module>/impl/<implName> (một triển khai cụ thể của giao diện mô-đun)
  • <module>/doc (Tài liệu cho việc sử dụng mô-đun)
  • <module>/tb (mã kiểm tra đơn vị cho mô-đun)

nơi <module>được đặt trong kho lưu trữ theo lớp mà nó thuộc về.


5
+1: Bố cục cây nguồn sẽ phản ánh kiến ​​trúc - một điều hiển nhiên mà tôi đang xem.
kiểm tra123

Làm thế nào để bạn quản lý các tệp an toàn - các tệp mà chỉ người dùng được ủy quyền mới có thể truy cập đăng nhập bài?
kiểm tra123

@ check123 Tôi không chắc là tôi hiểu câu hỏi. Tôi tập trung vào việc tổ chức các mô-đun nguồn, thay vì hỗ trợ các tệp cho dự án và mã nguồn thường được mọi người dự định truy cập. (Có trường hợp ngoại lệ và tôi sử dụng một thư mục dist / trên tất cả các mã với các hạn chế sử dụng / sửa đổi không chuẩn.)
Aidan Cully

48

Tôi thực sự không thể cho bạn nhiều lời khuyên liên quan đến các dự án web, nhưng đây là cách tôi cấu trúc cây của mình trong một dự án lập trình (chủ yếu theo quan điểm C / C ++):

  • /
    • src - Các tệp nguồn được viết bởi chính tôi
    • ext - Chứa các thư viện của bên thứ ba
      • tên gọi-1.2.8
        • bao gồm - Tiêu đề
        • lib - Tập tin lib được biên dịch
        • Donwload.txt - Chứa liên kết để tải xuống phiên bản đã sử dụng
    • ide - Tôi lưu trữ các tập tin dự án ở đây
      • vc10 - Tôi sắp xếp các tệp dự án tùy thuộc vào IDE
    • bin - exe biên dịch đi đây
    • build - Các tập tin xây dựng của trình biên dịch
    • doc - Tài liệu dưới mọi hình thức
    • Sẵn sàng
    • TẢI VỀ
    • SAO CHÉP

Một vài lưu ý:

  1. Nếu tôi đang viết thư viện (và tôi đang sử dụng C / C ++) thì trước tiên tôi sẽ tổ chức các tệp nguồn của mình trong hai thư mục có tên là "bao gồm" và "src" và sau đó theo mô-đun. Nếu đó là một ứng dụng, thì tôi sẽ tổ chức chúng theo mô-đun (các tiêu đề và nguồn sẽ đi trong cùng một thư mục).

  2. Các tệp và thư mục mà tôi đã liệt kê ở trên in nghiêng tôi sẽ không thêm vào kho lưu trữ mã.


Sự khác biệt giữa idebuild là gì?
M. Dudley

3
idechỉ là nơi tôi lưu trữ các tập tin dự án. buildchứa các tệp đối tượng được tạo bởi trình biên dịch. Các IDE khác nhau có thể sử dụng cùng một trình biên dịch, vì vậy đó là lý do tại sao tôi giữ các tệp dự án IDE tách biệt với các tệp đối tượng được xây dựng bởi trình biên dịch.
Paul

vì vậy build == obj (thuật ngữ được sử dụng bởi nhiều hệ thống khác)
gbjbaanb

@gbjbaanb Vâng, tôi đoán thế. Nó không thực sự quan trọng vì thư mục đó không được đẩy vào kho lưu trữ. :) Tôi gọi nó là 'build' bởi vì đó là cái mà IDE tôi đang sử dụng gọi là att (Visual Studio).
Paul

Điều gì nếu exe của bạn cần một số dll để chạy? Bạn có sao chép tất cả các tập tin dll vào cùng một thư mục như exe không? Bạn có sử dụng một số sự kiện sau khi xây dựng không?
Wakan Tanka

14

Mặc dù Bố cục thư mục tiêu chuẩn Maven dành riêng cho Java, nhưng nó cũng có thể là cơ sở tốt cho các loại dự án khác.

Đây là cấu trúc cơ bản (bạn có thể thay thế các thư mục 'java' bằng 'php', 'cpp', v.v.):

src/main/java       Application/Library sources 
src/main/resources  Application/Library resources  
src/main/filters    Resource filter files 
src/main/assembly   Assembly descriptors 
src/main/config     Configuration files 
src/main/webapp     Web application sources 
src/test/java       Test sources 
src/test/resources  Test resources 
src/test/filters    Test resource filter files 
src/site            Site 
LICENSE.txt         Project's license 
NOTICE.txt          Notices and attributions required by libraries
README.txt          Project's readme

Cấu trúc cơ bản được chia thành 'src / main' và 'src / test' sau đó được nhóm theo loại.


5

Tôi không thực sự biết về các quy ước nhưng tất cả các dự án chính của tôi đều được thực hiện bằng Symfony Framework và tôi đã quen với cấu trúc cây như sau:

nguồn gốc/

  • ứng dụng
  • tên ứng dụng
    • cấu hình (tập tin cấu hình cụ thể của ứng dụng)
    • lib (tệp php cụ thể của ứng dụng)
    • mô-đun (phân phối mô-đun chức năng)
      • mô-đun tên
        • mẫu (html)
        • hành động (mã php)
  • confing (tập tin cấu hình dự án)
  • lib (mã php có thể được sử dụng trong dự án lỗ)
  • mô hình (các lớp đại diện cho thông tin dự án)
    • căn cứ
  • biểu mẫu (tệp php xử lý biểu mẫu, điều này có thể khá khó đạt được nếu không có symfony)
    • cơ sở (các lớp mẫu cơ sở)
  • web
  • css
    • hình ảnh
    • file.css
  • js
  • log (tệp nhật ký có thể được tạo)
  • dữ liệu (thông tin cụ thể về dữ liệu, như bản vá sql, hoặc bất cứ thứ gì)
  • sql
  • plugin (thư viện được sử dụng có thể được hợp nhất với bất kỳ ứng dụng nào của dự án)

Nếu bạn quan tâm, xin vui lòng đọc tài liệu về symfony về vấn đề này để tìm hiểu thêm ( MVC và Tổ chức mã trên Symfony ).


Thư mục CSS của bạn có tập trung không? Tôi có nghĩa là tất cả CSS của bạn (trên toàn dự án) nằm trong cùng một thư mục?
kiểm tra123

Không nhất thiết, bạn có thể chia nó, nhưng vì hầu hết các dự án của tôi có xu hướng chỉ có 2 ứng dụng (frontend và backend), không có nhiều tệp css (plugin luôn có thư mục web riêng để trừu tượng hóa)
guiman

5

Lý tưởng nhất, tổ chức có một kho lưu trữ duy nhất, cấu trúc trong đó nhằm tăng cường sự tham gia giữa kỹ thuật & kinh doanh và thúc đẩy tái sử dụng.

...\products\
...\products\productName\
...\products\productName\doc\

...\systems\
...\systems\systemName\
...\systems\systemName\doc\
...\systems\systemName\res\
...\systems\systemName\build\
...\systems\systemName\test\

...\library\
...\library\libraryName\
...\library\libraryName\doc\
...\library\libraryName\build\
...\library\libraryName\test\

...\devops\

các sản phẩm

Một thư mục cho mỗi sản phẩm; giúp truyền đạt cách phần mềm hỗ trợ doanh nghiệp.

Lý tưởng nhất, mỗi "sản phẩm" không chỉ là một tệp cấu hình cho biết hệ thống nào sẽ được gọi và cách chúng được cấu hình. Thư mục con doc có thể chứa tóm tắt cấp cao nhất \ spec & bất kỳ tài liệu quảng cáo nào, v.v ...

Bằng cách tách biệt các sản phẩm và hệ thống, chúng tôi truyền đạt tiềm năng tái sử dụng cho phía khách hàng của doanh nghiệp và phá vỡ các silo trên mỗi sản phẩm. (Điều này trái ngược với cách tiếp cận "dòng sản phẩm" cho cùng một vấn đề)

hệ thống

Một thư mục trên mỗi hệ thống; giúp truyền đạt các khả năng chính & cơ hội / giá trị của nội dung của kho lưu trữ.

  1. Tập tin "Quản lý cấu hình" chỉ định môi trường xây dựng và triển khai.
  2. Cấu hình thử nghiệm cấp hệ thống (có thể là số lượng đáng kể).
  3. Logic & chức năng cấp cao nhất; nâng nặng nhất được thực hiện bởi các chức năng thư viện.

thư viện

Các thành phần tái sử dụng được gọi bởi các hệ thống khác nhau. Hầu hết các hoạt động phát triển được tổ chức xung quanh việc sản xuất các thư viện, thay vì các hệ thống, vì vậy việc tái sử dụng được "đưa vào" trong quá trình phát triển.

tín đồ

Xây dựng, tích hợp liên tục và chức năng tự động hóa phát triển khác.

Phần kết luận

Cây nguồn là một phần chính của tài liệu và định hình cách tiếp cận, cấu trúc và tâm lý của mối quan hệ của doanh nghiệp với công nghệ độc quyền của nó.

Các trình điều khiển cho phương pháp này được giải thích sâu hơn một chút trong câu trả lời của tôi cho câu hỏi này: https://softwareengineering.stackexchange.com/questions/43733/who- Organizes-your-matlab-code / 59637 # 59637


Lưu ý: Có thể hữu ích khi đặt tên các thư mục theo cách tương thích với loại phân cấp sản phẩm được thảo luận trong sổ tay kỹ thuật hệ thống INCOSE.
William Payne

3

Những gì tôi đang cố gắng làm cho mỗi dự án tương tự như:

  • src - tệp nguồn, một thư mục cho mỗi không gian tên / gói để dễ dàng truy xuất tệp (ngay cả tệp tiêu đề cho C / C ++)
  • ext - đối với các thư viện bên ngoài / thư viện của bên thứ ba, thật đơn giản để thêm các phần bên ngoài (chẳng hạn như kho SVN). Bên trong, một thư mục cho mỗi thư viện (nhị phân và bao gồm các tệp)
  • bin - cho các nhị phân được xây dựng, có thể nhanh chóng được xuất khẩu để phát hành
    • inc - cho tệp tiêu đề C / C ++ (được sao chép bởi IDE / makefile / etc ...)
  • ra - cho tất cả các tệp được tạo tạm thời (. class, .obj, v.v.) và nó có thể bị bỏ qua (ví dụ: SVN)
  • doc - cho mọi tài liệu, thường được tạo bằng Doxygen
  • res - bằng cách đặt tài nguyên ở đây, có thể tách các tệp nguồn văn bản và tài nguyên nhị phân được sử dụng bởi chương trình. Tôi không thực sự có thứ bậc cụ thể bên trong.
    • cấu hình - cho một số tập tin cấu hình
    • drawable - đối với một số hình ảnh hoặc biểu tượng

Tất cả các tệp hoặc tệp tạo tệp IDE được lưu trực tiếp tại thư mục gốc nếu bạn chỉ sử dụng một trong số chúng.


2

Tôi làm một cái gì đó như thế này. Hoạt động tốt cho một trò chơi đa nền tảng Tôi đang làm trong thời gian rảnh rỗi. Thật không may trong công việc, mọi thứ được tổ chức ít hơn nhiều ...

Output                      <-- Build outputs
Docs
External
   <libname>
      Include
      Lib
Data
<ProjectName>.xcodeproj
<ProjectName>VS2010
Source
Temp                        <-- Intermediate stuff from builds and other tools
Tools

2

Đối với các nhóm của tôi, chúng tôi cố gắng thực thi một cấu trúc tiêu chuẩn trong các dự án để dễ dàng tìm thấy mọi thứ khi nhóm chuyển ngữ cảnh và để tránh phải học lại mỗi lần. Không phải tất cả các dự án đều cần tất cả các hệ thống, vì vậy chúng tôi bắt đầu với tập tối thiểu.

/ Nguồn / Thành phần / Ngôn ngữ

/ Nguồn / Thành phần / Bên thứ 3 /

/ Tài liệu / Yêu cầu

/ Tài liệu / Thiết kế

/ Kiểm tra / Tự động / Đơn vị

/ Kiểm tra / Tự động / Tên công cụ

/ Kiểm tra / Hướng dẫn

Điều này dẫn đến một số trùng lặp, đặc biệt là theo mã và thư viện của bên thứ 3, nhưng ít nhất chúng tôi không bao giờ quên câu trả lời cho một cái gì đó như "Cái gì sử dụng Trình soạn thảo RogueWave?"


1
Các thành phần đường dẫn viết hoa trông thực sự ngớ ngẩn và vô nghĩa đối với tôi. Tại sao rất nhiều chữ thường? Việc gõ cho con người dễ dàng hơn nhiều (trong khi các công cụ không quan tâm, bao gồm cả trình quản lý tệp WIMP ) và đọc tốt như nhau nhờ các dấu tách đường dẫn. Một chiến thắng dứt khoát với tôi.
ulidtko

2

Tôi thích những ý tưởng được trình bày trong trang này www.javapractices.com/topic/TopicAction.do?Id=205 . Về cơ bản, khuyến nghị là tổ chức dự án của bạn thành các tính năng (hoặc mô-đun, thành phần). Ngoài các lý do được trình bày ở đó:

  1. Tải ít nhận thức hơn khi bạn nghĩ về phạm vi của mã bạn đang làm việc vì bạn có đảm bảo rằng bất kỳ mã nào trong tính năng bạn đang làm việc là "tính năng riêng tư".
  2. Có một cảm giác an toàn bổ sung khi bạn được đảm bảo rằng bạn chỉ sửa đổi mã cho một tính năng nhất định. Ví dụ: bạn sẽ không phá vỡ bất cứ thứ gì ngoài tính năng bạn đang làm việc. Một lần nữa điều này là do "tính năng riêng tư".
  3. Tải nhận thức ít hơn đơn giản vì có ít tệp bạn có thể thấy cho một gói nhất định. Tôi chắc chắn rằng mọi người đã thấy một gói chứa hơn 15 tệp.

Lưu ý điều này tập trung vào các gói Java (còn gọi là không gian tên). Đối với các dự án lớn, tôi khuyên bạn, vì những lý do tương tự, chia dự án thành nhiều dự án (như trong nhiều dự án maven) đại diện cho một tính năng kinh doanh. Đối với các dự án maven, tôi khuyên bạn nên đọc này .

Cho đến nay, các dự án tôi đã / đang tham gia không theo những dự án này. Có nhiều lý do, nhưng đây là một vài lý do:

  1. Sự hiểu lầm của công cụ sửa đổi truy cập mặc định Java (phần lớn công cụ sửa đổi truy cập bị hiểu nhầm theo cuốn sách này )
  2. "Argumentum ad populum": Văn hóa ưu tiên của từng lớp (có thể do Lý do số 1)

Tôi nghĩ rằng có một cơ hội bị bỏ lỡ để ngăn chặn sự phức tạp nếu tổ chức nguồn dự án không được thực hiện nghiêm túc khi bắt đầu dự án như kiến ​​trúc sư Alexander nói:

"Như bất kỳ nhà thiết kế nào cũng sẽ nói với bạn, đó là những bước đầu tiên trong quy trình thiết kế chiếm phần lớn. Một vài nét đầu tiên, tạo ra hình thức, mang trong mình số phận của phần còn lại." - Christopher Alexander

Tùy thuộc vào quy mô và mức độ phức tạp của một dự án, cơ hội bị bỏ lỡ để cắt giảm chi phí hoặc ROI có thể thực sự lớn. (Tôi muốn xem một nghiên cứu để xem con số chính xác cho việc này)


2

Đề nghị của tôi là tải xuống nhiều khung hoặc công cụ khác nhau và xem các nhóm phát triển khổng lồ xử lý bố cục thư mục của họ như thế nào.

Có rất nhiều cách để tổ chức các tệp mà tốt hơn là chọn một tệp và cố gắng bám vào nó tại bất kỳ dự án nào. Bám sát một quy ước cụ thể cho đến khi hoàn thành hoặc sửa chữa để tránh lỗi và mất thời gian không cần thiết.

Bạn có thể tải xuống các khung công tác Laravel, Symphony hoặc Codeigniter cho các dự án web để có bố cục thư mục tức thì hoạt động.

Vì vậy, tôi sẽ cố gắng truyền đạt một bố cục thư mục chung cho bất kỳ sự phát triển nào:

MVC (Model View Controller) đưa ra một mô hình tổ chức tốt.

Mã nguồn gốc có thể là src (C ++) hoặc ứng dụng (phát triển web)

Một cấu trúc tệp không có mục tiêu rõ ràng cho các lớp mà nhóm chắc chắn sẽ gây nhầm lẫn. Không chỉ tổ chức mã, thay vào đó, nó có thể duy trì các trình tải tự động, nhà máy lớp, bọc bộ nhớ cục bộ, lưu trữ từ xa và không gian tên.

Cấu trúc thư mục này có nguồn gốc và được đơn giản hóa từ Khung làm việc . Sở thích của tôi về bài đăng này là đặt tên số nhiều nhưng tôi sử dụng các từ số ít trong các dự án của tôi.


src / Storage (mô hình / lưu trữ tệp / api / mysql / sql-lite / memcached / redis triển khai)

src / kho lưu trữ (Trình bao bọc của 'triển khai lưu trữ' với một số logic lưu trữ, giao diện chung và quy ước kết quả trả về.)

src / dịch vụ | logic | thực thể (logic kinh doanh ứng dụng)

src / bộ điều khiển (Được sử dụng trong phát triển web để định tuyến các yêu cầu máy chủ đến dịch vụ của bạn)

src / mô-đun | hệ thống (Hệ thống mô đun mở rộng chức năng chung khung của bạn. Dịch vụ có thể sử dụng các mô-đun nhưng không phải ngược lại)

src / helpers (Các lớp trợ giúp hoặc trình bao bọc, ví dụ như thao tác chuỗi. Nhiều lần điều này có thể xảy ra trên libs | nhà cung cấp khi bên thứ ba)

src / loại (enum đặt tên)

công cộng | xây dựng | đầu ra (web hoặc c ++)

config (Cài đặt tệp. YAML đang trở nên phổ biến đối với các tệp cấu hình đa nền tảng)

bộ nhớ cache

nhật ký

lang (en / es / ru / ...)

bootstrap (Bắt đầu khung và ứng dụng)

tài liệu (Tài liệu được viết ở định dạng markdown .md)

kiểm tra (Kiểm tra đơn vị)

cơ sở dữ liệu / di chuyển (Tạo cấu trúc cơ sở dữ liệu từ đầu)

cơ sở dữ liệu / hạt giống (Điền cơ sở dữ liệu của bạn với dữ liệu giả để kiểm tra)

libs | nhà cung cấp (tất cả phần mềm của bên thứ ba. 'libs' trên C ++ và 'nhà cung cấp' thường trên php)

tài sản | tài nguyên (hình ảnh / âm thanh / script / json / bất kỳ phương tiện truyền thông nào)


1

Với các ngôn ngữ hướng đối tượng, bạn có khả năng xây dựng các không gian tên. Phân tích logic đó được sử dụng để phân tách các phần của ứng dụng để tránh ghép nối là nguồn chính của phân tích vị trí tệp logic. Sử dụng khớp nối làm lý do để phá vỡ các không gian tên là một nơi tốt để bắt đầu http://en.wikipedia.org/wiki/Software_package_metrics .

Những người khác đã nói về việc thiết lập dự án liên quan đến xây dựng, nhưng một khi bạn vào chính nguồn đó, thì điều đó có ý nghĩa - chỉ cần sử dụng cách bạn phân tách logic một cách hợp lý.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.