Làm thế nào một lớp `Nhân viên 'được thiết kế?


11

Tôi đang cố gắng tạo ra một chương trình để quản lý nhân viên. Tôi không thể, tuy nhiên, tìm ra cách thiết kế Employeelớp. Mục tiêu của tôi là có thể tạo và thao tác dữ liệu nhân viên trên cơ sở dữ liệu bằng cách sử dụng một Employeeđối tượng.

Việc thực hiện cơ bản mà tôi nghĩ ra, là cách đơn giản này:

class Employee
{
    // Employee data (let's say, dozens of properties).

    Employee() {}
    Create() {}
    Update() {}
    Delete() {}
}

Sử dụng thực hiện này, tôi gặp một số vấn đề.

  1. IDsở dữ liệu của một nhân viên được cung cấp bởi cơ sở dữ liệu, vì vậy nếu tôi sử dụng đối tượng để mô tả một nhân viên mới, sẽ không có IDđể lưu trữ, trong khi một đối tượng đại diện cho một nhân viên hiện tại sẽID. Vì vậy, tôi có một tài sản đôi khi mô tả đối tượng và đôi khi không (Điều này có thể cho thấy chúng tôi vi phạm SRP ? Vì chúng tôi sử dụng cùng một lớp để đại diện cho nhân viên mới và nhân viên hiện tại ...).
  2. Các Createphương pháp được cho là để tạo ra một nhân viên trên cơ sở dữ liệu, trong khi UpdateDeletecó nghĩa vụ phải hành động dựa trên một nhân viên hiện có (Một lần nữa, SRP ...).
  3. Phương thức 'Tạo' nên có các tham số nào? Hàng tá thông số cho tất cả dữ liệu nhân viên hoặc có thể là một Employeeđối tượng?
  4. Lớp học có nên bất biến?
  5. UpdateCông việc sẽ thế nào ? Nó sẽ lấy các thuộc tính và cập nhật cơ sở dữ liệu? Hoặc có thể nó sẽ lấy hai đối tượng - một "cũ" và một "mới" và cập nhật cơ sở dữ liệu với sự khác biệt giữa chúng? (Tôi nghĩ rằng câu trả lời phải làm với câu trả lời về tính biến đổi của lớp).
  6. Điều gì sẽ là trách nhiệm của các nhà xây dựng? Các tham số cần có là gì? Nó sẽ lấy dữ liệu nhân viên từ cơ sở dữ liệu bằng cách sử dụng một idtham số và họ điền các thuộc tính?

Vì vậy, như bạn có thể thấy, tôi có một chút hỗn độn trong đầu, và tôi rất bối rối. Bạn có thể vui lòng giúp tôi hiểu một lớp như vậy sẽ như thế nào?

Xin lưu ý rằng tôi không muốn có ý kiến, chỉ để hiểu làm thế nào một lớp thường được sử dụng như vậy thường được thiết kế.


3
Vi phạm nghiêm trọng của bạn đối với SRP là bạn có một lớp đại diện cho cả một thực thể và chịu trách nhiệm về logic CRUD. Nếu bạn tách nó ra, các hoạt động CRUD và cấu trúc thực thể sẽ là các lớp khác nhau, thì 1.2. không phá vỡ SRP. 3. nên lấy một Employeeđối tượng để cung cấp sự trừu tượng hóa, các câu hỏi 4.5. nói chung là không thể trả lời được, tùy thuộc vào nhu cầu của bạn và nếu bạn tách cấu trúc và các hoạt động CRUD thành hai lớp, thì nó khá rõ ràng, hàm tạo của Employeedữ liệu không thể tìm nạp dữ liệu từ db nữa, để câu trả lời 6.
Andy

@DavidPacker - Cảm ơn. Bạn có thể đặt nó trong một câu trả lời?
Sipo

5
Đừng, tôi nhắc lại, đừng để ctor của bạn tiếp cận với cơ sở dữ liệu. Làm như vậy kết hợp chặt chẽ mã vào cơ sở dữ liệu và làm cho mọi thứ trở nên khó kiểm tra (thậm chí kiểm tra thủ công trở nên khó khăn hơn). Nhìn vào mẫu kho lưu trữ. Hãy suy nghĩ về nó trong một giây, bạn Updatelà một nhân viên, hoặc bạn cập nhật một hồ sơ nhân viên? Bạn Employee.Delete(), hay làm một Boss.Fire(employee)?
RubberDuck

1
Ngoài những gì đã được đề cập, nó có ý nghĩa với bạn rằng bạn cần một nhân viên để tạo ra một nhân viên? Trong hồ sơ hoạt động, có thể có ý nghĩa hơn khi làm mới Nhân viên và sau đó gọi Lưu trên đối tượng đó. Mặc dù sau đó, mặc dù, bây giờ bạn có một lớp chịu trách nhiệm về logic kinh doanh cũng như sự kiên trì dữ liệu của chính nó.
Ông Cochese

Câu trả lời:


10

Đây là một phiên âm được hình thành tốt hơn của nhận xét ban đầu của tôi dưới câu hỏi của bạn. Câu trả lời cho các câu hỏi của OP có thể được tìm thấy ở dưới cùng của câu trả lời này. Ngoài ra xin vui lòng kiểm tra các lưu ý quan trọng nằm ở cùng một nơi.


Những gì bạn hiện đang mô tả, Sipo, là một mẫu thiết kế được gọi là Bản ghi hoạt động . Như với tất cả mọi thứ, ngay cả cái này đã tìm thấy vị trí của nó trong số các lập trình viên, nhưng đã bị loại bỏ để ủng hộ kho lưu trữ và các mẫu ánh xạ dữ liệu vì một lý do đơn giản, khả năng mở rộng.

Nói tóm lại, một bản ghi hoạt động là một đối tượng, trong đó:

  • đại diện cho một đối tượng trong miền của bạn (bao gồm các quy tắc kinh doanh, biết cách xử lý các hoạt động nhất định trên đối tượng, chẳng hạn như nếu bạn có thể hoặc không thể thay đổi tên người dùng, v.v.),
  • biết cách lấy, cập nhật, lưu và xóa thực thể.

Bạn giải quyết một số vấn đề với thiết kế hiện tại của bạn và vấn đề chính của thiết kế của bạn được giải quyết ở điểm cuối cùng, thứ 6, (cuối cùng nhưng không kém phần quan trọng, tôi đoán vậy). Khi bạn có một lớp mà bạn đang thiết kế một hàm tạo và bạn thậm chí không biết hàm tạo nên làm gì, thì lớp đó có thể đang làm gì đó sai. Điều đó đã xảy ra trong trường hợp của bạn.

Nhưng việc sửa thiết kế thực sự khá đơn giản bằng cách chia đại diện thực thể và logic CRUD thành hai (hoặc nhiều) lớp.

Đây là những gì thiết kế của bạn trông giống như bây giờ:

  • Employee- chứa thông tin về cấu trúc nhân viên (thuộc tính của nó) và phương thức sửa đổi thực thể (nếu bạn quyết định đi theo cách có thể thay đổi), chứa logic CRUD cho Employeethực thể, có thể trả về danh sách các Employeeđối tượng, chấp nhận một Employeeđối tượng khi bạn muốn cập nhật một nhân viên, có thể trả lại một Employeethông qua một phương thức nhưgetSingleById(id : string) : Employee

Wow, lớp học có vẻ rất lớn.

Đây sẽ là giải pháp được đề xuất:

  • Employee - chứa thông tin về cấu trúc nhân viên (thuộc tính của nó) và phương pháp làm thế nào để sửa đổi thực thể (nếu bạn quyết định đi theo cách có thể thay đổi)
  • EmployeeRepository- chứa logic CRUD cho Employeethực thể, có thể trả về danh sách các Employeeđối tượng, chấp nhận một Employeeđối tượng khi bạn muốn cập nhật một nhân viên, có thể trả về một đối tượng Employeethông qua một phương thức nhưgetSingleById(id : string) : Employee

Bạn đã nghe nói về sự tách biệt của mối quan tâm ? Không, bạn sẽ bây giờ. Đây là phiên bản ít nghiêm ngặt hơn của Nguyên tắc Trách nhiệm duy nhất, nói rằng một lớp học thực sự chỉ có một trách nhiệm, hoặc như chú Bob nói:

Một mô-đun nên có một và chỉ một lý do để thay đổi.

Một điều khá rõ ràng là nếu tôi có thể phân chia rõ ràng lớp ban đầu của bạn thành hai lớp vẫn có giao diện được làm tròn tốt, thì lớp ban đầu có thể đã làm quá nhiều, và nó đã được.

Điều tuyệt vời về mẫu kho lưu trữ, nó không chỉ đóng vai trò là sự trừu tượng để cung cấp một lớp giữa giữa cơ sở dữ liệu (có thể là bất cứ thứ gì, tệp, noQuery, SQL, hướng đối tượng), mà thậm chí không cần phải cụ thể lớp học. Trong nhiều ngôn ngữ OO, bạn có thể định nghĩa giao diện là một thực tế interface(hoặc một lớp với một phương thức ảo thuần túy nếu bạn ở trong C ++) và sau đó có nhiều triển khai.

Điều này hoàn toàn đưa ra quyết định cho dù một kho lưu trữ là một triển khai thực tế của bạn chỉ đơn giản là dựa vào giao diện bằng cách thực sự dựa vào cấu trúc với interfacetừ khóa. Và kho lưu trữ chính xác là như vậy, nó là một thuật ngữ ưa thích cho sự trừu tượng hóa lớp dữ liệu, cụ thể là ánh xạ dữ liệu vào miền của bạn và ngược lại.

Một điều tuyệt vời khác về việc tách nó thành (ít nhất) hai lớp là bây giờ Employeelớp có thể quản lý rõ ràng dữ liệu của chính nó và thực hiện nó rất tốt, bởi vì nó không cần phải quan tâm đến những điều khó khăn khác.

Câu 6: Vậy nhà xây dựng nên làm gì trong lớp vừa tạo Employee? Nó đơn giản. Cần lấy các đối số, kiểm tra xem chúng có hợp lệ không (chẳng hạn như độ tuổi không nên âm hoặc tên không nên trống), đưa ra lỗi khi dữ liệu không hợp lệ và nếu xác thực được chuyển, hãy gán đối số cho các biến riêng tư của thực thể. Bây giờ nó không thể giao tiếp với cơ sở dữ liệu, vì đơn giản là nó không biết làm thế nào để làm điều đó.


Câu hỏi 4: Không thể trả lời được, nói chung, vì câu trả lời phụ thuộc rất nhiều vào chính xác những gì bạn cần.


Câu hỏi 5: Bây giờ bạn đã tách lớp cồng kềnh thành hai, bạn có thể có nhiều phương thức cập nhật trực tiếp trên Employeelớp, như changeUsername, markAsDeceasedsẽ thao tác dữ liệu của Employeelớp chỉ trong RAM và sau đó bạn có thể giới thiệu một phương thức như registerDirtytừ Mẫu đơn vị công việc cho lớp kho lưu trữ, thông qua đó bạn sẽ cho kho lưu trữ biết rằng đối tượng này đã thay đổi thuộc tính và sẽ cần được cập nhật sau khi bạn gọi commitphương thức.

Rõ ràng, đối với một bản cập nhật, một đối tượng yêu cầu phải có id và do đó đã được lưu và đó là khả năng đáp ứng của kho lưu trữ để phát hiện điều này và gây ra lỗi khi các tiêu chí không được đáp ứng.


Câu hỏi 3: Nếu bạn quyết định chọn mẫu Đơn vị công việc, createphương thức sẽ là registerNew. Nếu bạn không, tôi có thể gọi nó savethay thế. Mục tiêu của kho lưu trữ là cung cấp một sự trừu tượng giữa miền và lớp dữ liệu, vì điều này tôi sẽ khuyên bạn rằng phương thức này (có thể registerNewhoặc save) chấp nhận Employeeđối tượng và tùy thuộc vào các lớp thực hiện giao diện kho lưu trữ, thuộc tính nào họ quyết định đưa ra khỏi thực thể. Vượt qua toàn bộ một đối tượng sẽ tốt hơn nên bạn không cần phải có nhiều tham số tùy chọn.


Câu hỏi 2: Cả hai phương thức bây giờ sẽ là một phần của giao diện kho lưu trữ và chúng không vi phạm nguyên tắc trách nhiệm duy nhất. Trách nhiệm của kho lưu trữ là cung cấp các hoạt động CRUD cho các Employeeđối tượng, đó là những gì nó làm (ngoài Đọc và Xóa, CRUD dịch sang cả Tạo và Cập nhật). Rõ ràng, bạn có thể phân chia kho lưu trữ hơn nữa bằng cách có EmployeeUpdateRepositoryvà không, nhưng điều đó hiếm khi cần thiết và một triển khai duy nhất thường có thể chứa tất cả các hoạt động CRUD.


Câu hỏi 1: Bạn đã kết thúc với một Employeelớp đơn giản mà bây giờ (trong số các thuộc tính khác) có id. Việc id được điền hay trống (hoặc null) tùy thuộc vào việc đối tượng đã được lưu hay chưa. Tuy nhiên, id vẫn là một thuộc tính mà thực thể sở hữu và trách nhiệm của Employeethực thể là chăm sóc các thuộc tính của nó, do đó chăm sóc id của nó.

Cho dù một thực thể có hoặc không có id thường không quan trọng cho đến khi bạn cố gắng thực hiện một số logic-kiên trì trên nó. Như đã đề cập trong câu trả lời cho câu hỏi 5, trách nhiệm của kho lưu trữ là phát hiện bạn không cố lưu một thực thể đã được lưu hoặc cố cập nhật một thực thể mà không có id.


Lưu ý quan trọng

Xin lưu ý rằng mặc dù việc phân tách các mối quan tâm là rất tốt, nhưng thực sự thiết kế một lớp kho lưu trữ chức năng là một công việc khá tẻ nhạt và theo kinh nghiệm của tôi thì khó khăn hơn một chút so với cách tiếp cận hồ sơ hoạt động. Nhưng bạn sẽ kết thúc với một thiết kế linh hoạt hơn và có thể mở rộng, đó có thể là một điều tốt.


Hmm giống như câu trả lời của tôi, nhưng không phải là 'góc cạnh' mang sắc thái
Ewan

2
@Ewan Tôi không đánh giá thấp câu trả lời của bạn, nhưng tôi có thể thấy lý do tại sao một số có thể có. Nó không trả lời trực tiếp một số câu hỏi của OP và một số đề xuất của bạn dường như không có cơ sở.
Andy

1
Câu trả lời hay và đầy đủ. Đánh vào đầu đinh với sự tách biệt của mối quan tâm. Và tôi thích cảnh báo rằng pintpoint là sự lựa chọn quan trọng để thực hiện giữa một thiết kế phức tạp hoàn hảo và một sự thỏa hiệp tốt đẹp.
Christophe

Đúng, câu trả lời của bạn là vượt trội
Ewan

khi lần đầu tiên tạo một đối tượng nhân viên mới, sẽ không có giá trị đối với ID. Trường id có thể để lại với giá trị null nhưng nó sẽ khiến đối tượng nhân viên ở trạng thái không hợp lệ ????
Susantha7

2

Đầu tiên tạo một cấu trúc nhân viên chứa các thuộc tính của nhân viên khái niệm.

Sau đó, tạo một cơ sở dữ liệu với cấu trúc bảng phù hợp, ví dụ mssql

Sau đó, tạo một kho lưu trữ nhân viên cho cơ sở dữ liệu đó EmployeeRepoMsSql với các hoạt động CRUD khác nhau mà bạn yêu cầu.

Sau đó tạo giao diện IEmployeeRepo hiển thị các hoạt động CRUD

Sau đó mở rộng cấu trúc Nhân viên của bạn thành một lớp với tham số xây dựng là IEmployeeRepo. Thêm các phương thức Lưu / Xóa khác nhau mà bạn yêu cầu và sử dụng EmployeeRepo được tiêm để triển khai chúng.

Khi nó chuyển sang Id tôi đề nghị bạn sử dụng GUID có thể được tạo thông qua mã trong hàm tạo.

Để làm việc với các đối tượng hiện có, mã của bạn có thể truy xuất chúng từ cơ sở dữ liệu thông qua kho lưu trữ trước khi gọi Phương thức cập nhật của chúng.

Ngoài ra, bạn có thể chọn mô hình đối tượng (nhưng theo quan điểm của tôi là ưu việt) Mô hình đối tượng miền thiếu máu nơi bạn không thêm phương thức CRUD vào đối tượng của mình và chỉ cần chuyển đối tượng đến repo để được cập nhật / lưu / xóa

Tính không thay đổi là một lựa chọn thiết kế sẽ phụ thuộc vào mẫu và kiểu mã hóa của bạn. Nếu bạn đang đi tất cả các chức năng thì cố gắng là bất biến là tốt. Nhưng nếu bạn không chắc chắn một đối tượng có thể thay đổi có lẽ dễ thực hiện hơn.

Thay vì Tạo () tôi sẽ đi với Save (). Tạo các tác phẩm với khái niệm bất biến, nhưng tôi luôn thấy hữu ích khi có thể xây dựng một đối tượng chưa 'Đã lưu', ví dụ: bạn có một số UI cho phép bạn điền vào một đối tượng hoặc đối tượng nhân viên và sau đó xác minh lại một số quy tắc trước đó lưu vào cơ sở dữ liệu.

***** mã ví dụ

public class Employee
{
    public string Id { get; set; }

    public string Name { get; set; }

    private IEmployeeRepo repo;

    //with the OOP approach you want the save method to be on the Employee Object
    //so you inject the IEmployeeRepo in the Employee constructor
    public Employee(IEmployeeRepo repo)
    {
        this.repo = repo;
        this.Id = Guid.NewGuid().ToString();
    }

    public bool Save()
    {
        return repo.Save(this);
    }
}

public interface IEmployeeRepo
{
    bool Save(Employee employee);

    Employee Get(string employeeId);
}

public class EmployeeRepoSql : IEmployeeRepo
{
    public Employee Get(string employeeId)
    {
        var sql = "Select * from Employee where Id=@Id";
        //more db code goes here
        Employee employee = new Employee(this);
        //populate object from datareader
        employee.Id = datareader["Id"].ToString();

    }

    public bool Save(Employee employee)
    {
        var sql = "Insert into Employee (....";
        //db logic
    }
}

public class MyADMProgram
{
    public void Main(string id)
    {
        //with ADM don't inject the repo into employee, just use it in your program
        IEmployeeRepo repo = new EmployeeRepoSql();
        var emp = repo.Get(id);

        //do business logic
        emp.Name = TextBoxNewName.Text;

        //save to DB
        repo.Save(emp);

    }
}

1
Mô hình miền thiếu máu có rất ít liên quan đến logic CRUD. Đây là một mô hình, mặc dù thuộc về lớp miền, không có chức năng và tất cả chức năng được phục vụ thông qua các dịch vụ, mà mô hình miền này đang được truyền dưới dạng tham số.
Andy

Chính xác, trong trường hợp này, repo là dịch vụ và các chức năng là các hoạt động CRUD.
Ewan

@DavidPacker bạn đang nói Mô hình miền thiếu máu là một điều tốt?
candied_orange

1
@CandiedOrange Tôi chưa đưa ra ý kiến ​​của mình trong bình luận, nhưng không, nếu bạn quyết định đi sâu vào ứng dụng của mình đến các lớp trong đó một lớp chỉ chịu trách nhiệm cho logic kinh doanh, tôi với ông Fowler rằng một mô hình miền thiếu máu trong thực tế là một mô hình chống. Tại sao tôi cần một UserUpdatedịch vụ với một changeUsername(User user, string newUsername)phương thức, khi tôi có thể thêm changeUsernamephương thức đó Usertrực tiếp vào lớp . Tạo ra một dịch vụ cho điều đó là vô nghĩa.
Andy

1
Tôi nghĩ rằng trong trường hợp này, việc tiêm repo chỉ để đưa logic CRUD vào Model là không tối ưu.
Ewan

1

Đánh giá thiết kế của bạn

EmployeeThực tế của bạn là một loại proxy cho một đối tượng được quản lý liên tục trong cơ sở dữ liệu.

Do đó, tôi đề nghị suy nghĩ với ID như thể nó là một tham chiếu đến đối tượng cơ sở dữ liệu của bạn. Với logic này, bạn có thể tiếp tục thiết kế của mình như đối với các đối tượng không phải là cơ sở dữ liệu, ID cho phép bạn triển khai logic thành phần truyền thống:

  • Nếu ID được đặt, bạn có một đối tượng cơ sở dữ liệu tương ứng.
  • Nếu ID không được đặt, không có đối tượng cơ sở dữ liệu tương ứng: Employeecó thể chưa được tạo hoặc có thể đã bị xóa.
  • Bạn cần một số cơ chế để bắt đầu mối quan hệ cho nhân viên cũ và xuất hiện các bản ghi cơ sở dữ liệu chưa được tải trong bộ nhớ.

Bạn cũng cần quản lý trạng thái cho đối tượng. Ví dụ:

  • khi một Nhân viên chưa được liên kết với một đối tượng DB thông qua việc tạo hoặc truy xuất dữ liệu, bạn sẽ không thể thực hiện cập nhật hoặc xóa
  • dữ liệu nhân viên trong đối tượng có đồng bộ với cơ sở dữ liệu hay có những thay đổi được thực hiện không?

Trong tâm trí này, chúng tôi có thể chọn:

class Employee
{
    ...
    Employee () {}       // Initialize an empty Employee
    Load(IDType ID) {}   // Load employee with known ID from the database
    bool Create() {}     // Create an new employee an set its ID 
    bool Update() {}     // Update the employee (can ID be changed?)
    bool Delete() {}     // Delete the employee (and reset ID because there's no corresponding ID. 
    bool isClean () {}   // true if ID empty or if all properties match database
}

Để có thể quản lý trạng thái đối tượng của bạn một cách đáng tin cậy, bạn phải đảm bảo đóng gói tốt hơn bằng cách đặt các thuộc tính riêng tư và chỉ cấp quyền truy cập thông qua getters và setters cập nhật trạng thái.

Những câu hỏi của bạn

  1. Tôi nghĩ rằng tài sản ID không vi phạm SRP. Trách nhiệm duy nhất của nó là đề cập đến một đối tượng cơ sở dữ liệu.

  2. Toàn bộ Nhân viên của bạn không tuân thủ SRP, vì nó chịu trách nhiệm liên kết với cơ sở dữ liệu, nhưng cũng giữ các thay đổi tạm thời và cho tất cả các giao dịch xảy ra với đối tượng đó.

    Một thiết kế khác có thể là giữ các trường có thể thay đổi trong một đối tượng khác sẽ chỉ được tải khi các trường cần được truy cập.

    Bạn có thể thực hiện các giao dịch cơ sở dữ liệu trên Nhân viên bằng cách sử dụng mẫu lệnh . Kiểu thiết kế này cũng sẽ giảm bớt sự tách rời giữa các đối tượng kinh doanh của bạn (Nhân viên) và hệ thống cơ sở dữ liệu cơ bản của bạn, bằng cách cách ly các thành ngữ và API cụ thể của cơ sở dữ liệu.

  3. Tôi sẽ không thêm hàng tá tham số vào Create(), bởi vì các đối tượng kinh doanh có thể phát triển và làm cho tất cả điều này rất khó để duy trì. Và mã sẽ trở nên không thể đọc được. Bạn có 2 lựa chọn ở đây: hoặc truyền một bộ tham số tối thiểu (không quá 4) hoàn toàn cần thiết để tạo một nhân viên trong cơ sở dữ liệu và thực hiện các thay đổi còn lại thông qua cập nhật, HOẶC bạn truyền một đối tượng. Nhân tiện, trong thiết kế của bạn tôi hiểu rằng bạn đã chọn : my_employee.Create().

  4. Lớp học có nên bất biến? Xem thảo luận ở trên: trong thiết kế ban đầu của bạn không. Tôi sẽ chọn một ID bất biến nhưng không phải là Nhân viên bất biến. Một nhân viên phát triển trong cuộc sống thực (vị trí công việc mới, địa chỉ mới, tình hình hôn nhân mới, thậm chí cả tên mới ...). Tôi nghĩ sẽ dễ dàng và tự nhiên hơn khi làm việc với thực tế này, ít nhất là trong lớp logic kinh doanh.

  5. Nếu bạn xem xét sử dụng một lệnh để cập nhật và một đối tượng riêng biệt cho (GUI?) Để giữ các thay đổi mong muốn, bạn có thể chọn cách tiếp cận cũ / mới. Trong tất cả các trường hợp khác, tôi sẽ chọn cập nhật một đối tượng có thể thay đổi. Chú ý: bản cập nhật có thể kích hoạt mã cơ sở dữ liệu để bạn cần đảm bảo sau khi cập nhật, đối tượng vẫn thực sự đồng bộ với DB.

  6. Tôi nghĩ rằng việc tìm nạp một nhân viên từ DB trong nhà xây dựng không phải là một ý tưởng hay, bởi vì việc tìm nạp có thể sai và trong nhiều ngôn ngữ, thật khó để đối phó với việc xây dựng thất bại. Trình xây dựng nên khởi tạo đối tượng (đặc biệt là ID) và trạng thái của nó.

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.