[Revit API] Thủ Thuật Lập Trình: Singleton - VietBIMCoder.com

[Revit API] Thủ Thuật Lập Trình : Singleton và trung tâm hóa lưu trữ dữ liệu

Singleton là một khuôn mẫu lập trình được định nghĩa để trung tâm hóa việc lưu trữ dữ liệu, giúp bạn truy xuất và xử lý dữ liệu một cách dễ dàng. Singleton đem lại hiệu quả rõ rệt khi bạn muốn quản lý có hệ thống trong một dự án phức tạp.

1.      Khái niệm ban đầu

Một singleton là một Class cho phép chỉ tồn tại một thể hiện của nó được tạo. Thể hiện Singleton tồn tại khi ứng dụng nằm trong bộ nhớ, và lưu trữ các dự liệu trong một Singleton sẽ giúp truy xuất và xử lý dữ liệu trong suốt hoạt động ứng dụng (chỉ bị loại bỏ khi hoàn thành hoặc tắt ứng dụng).

Singleton giúp bạn cách truy cập dữ liệu dễ dàng giữa các class điều khiển, quản lý dữ liệu dự án tốt hơn, nhờ đó tăng hiệu suất phát triển sản phẩm.

Singleton có thể áp dụng với nhiều ngôn ngữ lập trình hướng đối tượng khác nhau, ở đây mình sẽ sử dụng ngôn ngữ C# để mình họa.

2.      Thiết lập một Singleton:

a.       Tạo class Singleton với thuộc tính tĩnh Instance:

Thuộc tính Instance sẽ là thể hiện duy nhất của class Singleton dùng để lưu trữ và truy xuất dữ liệu dự án một cách tập trung.

namespace SingleData {  
    public class Singleton {  
        public static Singleton Instance {  
            get;  
            set;  
        }  
    }  
}

b.      Tạo thêm các thuộc tính dữ liệu cần để truy xuất:

Bạn có thể định nghĩa thuộc tính thuộc bất kì kiểu dữ liệu nào (Có thể Class trong thư viện RevitAPI, WPF… hoặc do bạn tự định nghĩa).

using Autodesk.Revit.ApplicationServices;  
using Autodesk.Revit.DB;  
using Autodesk.Revit.DB.Structure;  
using Autodesk.Revit.UI;  
using Autodesk.Revit.UI.Selection;  
using System.Collections.Generic;  
using System.Linq;  
namespace SingleData {  
    public class Singleton { 
        public static Singleton Instance { // Thể hiện tĩnh của Singleton   Remove featured image
            get;  
            set;  
        } 
        // Định nghĩa các biến thành viên  
        private Application application;  
        private UIDocument uiDocument;  
        private Document document;  
        private Selection selection;  
        private Transaction transaction;  
        private View activeView;  
        private List < Element > instanceElements;  
        private List < Element > typeElements;  
        private List < Rebar > rebars; 
        // biến thành viên khác …

        // Định nghĩa các thuộc tính để truy xuất  
        public UIApplication UIApplication {  
            get;  
            set;  
        }  
        public Rebar SelectedRebar {  
            get;  
            set;  
        }  
        public Application Application {  
            get {  
                if (application == null) application = UIApplication.Application;  
                return application;  
            }  
        }  
        public UIDocument UIDocument {  
            get {  
                if (uiDocument == null) uiDocument = UIApplication.ActiveUIDocument;  
                return uiDocument;  
            }  
        }  
        public Document Document {  
            get {  
                if (document == null) document = UIDocument.Document;  
                return document;  
            }  
        }  
        public Selection Selection {  
            get {  
                if (selection == null) selection = UIDocument.Selection;  
                return selection;  
            }  
        }  
        public Transaction Transaction {  
            get {  
                if (transaction == null) transaction = new Transaction(Document, "Add-in");  
                return transaction;  
            }  
        }  
        public View ActiveView {  
            get {  
                if (activeView == null) activeView = Document.ActiveView;  
                return activeView;  
            }  
        }  
        public List < Element > InstanceElements {  
            get {  
                if (instanceElements == null) instanceElements = new FilteredElementCollector(Document).WhereElementIsNotElementType().ToList();  
                return instanceElements;  
            }  
        }  
        public List < Element > TypeElements {  
            get {  
                if (typeElements == null) typeElements = new FilteredElementCollector(Document).WhereElementIsElementType().ToList();  
                return typeElements;  
            }  
        }  
        public List < Rebar > Rebars {  
            get {  
                if (rebars == null) rebars = InstanceElements.Where(x => x is Rebar).Cast < Rebar > ().ToList();  
                return rebars;  
            }  
        }  
        // thuộc tính thành viên khác …
    }  
}

c.       Khởi tạo thể hiện tĩnh, truy xuất và lưu trữ dữ liệu

Khởi tạo thể hiện bởi thuộc tính Instance của Singleton ở bất kì đâu trong dự án (nên ở vị trí khởi đầu dự án), gán các dự liệu cần thiết.

using Autodesk.Revit.Attributes;  
using Autodesk.Revit.DB;  
using Autodesk.Revit.UI; 
using System.Linq; 
using SingleData;  
namespace RevitAddin1 {  
    [Transaction(TransactionMode.Manual)] public class Command: IExternalCommand {  
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { // Khởi tạo thuộc tính tĩnh Instance đại diện cho vị trí lưu trữ dữ liệu  
            Singleton.Instance = new Singleton(); // Truy xuất thuộc tính UIApplication trong Singleton và gán bằng dữ liệu lấy được trong Revit  
            Singleton.Instance.UIApplication = commandData.Application;  
            return Result.Succeeded;  
        }  
    }  
}

 

Lúc này bạn đã có thể truy xuất và xử lý các dữ liệu cần thiết (ở đây bạn đã lấy và lưu trữ thành công dữ liệu từ Revit session hiện hành)

// Truy xuất thuộc tính Transaction và thực thi phương thức  
Singleton.Instance.Transaction.Start(); // Truy xuất thuộc tính SelectedRebar vá gán bằng giá trị đầu tiên // trong mảng truy xuất được từ thuộc tính Rebars  
Singleton.Instance.SelectedRebar = Singleton.Instance.Rebars.First(); // Truy xuất thuộc tính Selection và thực thi phương thi  
Singleton.Instance.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element); // Các truy xuất và xử lý khác ... // Truy xuất thuộc tính Transaction và thực thi phương thức  
Singleton.Instance.Transaction.Commit();

 

3.      Phân lớp dữ liệu trong Singleton

Dữ liệu lưu trữ tập trung trong Singleton có thể được tổ chức phân lớp để người phát triển quản lý dữ liệu dự án dễ dàng, đặc biệt hiệu quả trong các dự án phức tạp áp dụng nhiều công nghệ.

Ở ví dụ này, chúng ta sẽ phân loại dữ liệu thành 3 nhóm: RevitData – dữ liệu Revit, WPFData – dữ liệu WPF, OtherData – dữ liệu khác hoặc tự định nghĩa.

a.       Tạo class Singleton

Thực hiện tương tự mục 2.a.

namespace SingleData {  
    public class Singleton { // Thể hiện tĩnh của Singleton  
        public static Singleton Instance {  
            get;  
            set;  
        } // Định nghĩa các biến thành viên  
        private RevitData revitData;  
        private WPFData wpfData;  
        private OtherData otherData; // biên1 thành viên khác ... // Định nghĩa các thuộc tính để truy xuất  
        public RevitData RevitData {  
            get {  
                if (revitData == null) revitData = new RevitData();  
                return revitData;  
            }  
        }  
        public WPFData WPFData {  
            get {  
                if (wpfData == null) wpfData = new WPFData();  
                return wpfData;  
            }  
        }  
        public OtherData OtherData {  
            get {  
                if (otherData == null) otherData = new OtherData();  
                return otherData;  
            }  
        } // thuộc tính khác...  
    }  
}

 

b.      Tạo class RevitData

Ở đây kiểu dữ liệu Revit Data có nhiệm vụ lưu trữ các dữ liệu đầu vào từ Revit (chưa qua xử lý).

using Autodesk.Revit.ApplicationServices;  
using Autodesk.Revit.DB;  
using Autodesk.Revit.DB.Structure;  
using Autodesk.Revit.UI;  
using Autodesk.Revit.UI.Selection;  
using System.Collections.Generic;  
using System.Linq;  
namespace SingleData {  
    public class RevitData { // Định nghĩa các biến thành viên  
        private Application application;  
        private UIDocument uiDocument;  
        private Document document;  
        private Selection selection;  
        private Transaction transaction;  
        private View activeView;  
        private List < Element > instanceElements; // biên1 thành viên khác ... // Định nghĩa các thuộc tính để truy xuất  
        public UIApplication UIApplication {  
            get;  
            set;  
        }  
        public Rebar SelectedRebar {  
            get;  
            set;  
        }  
        public Application Application {  
            get {  
                if (application == null) application = UIApplication.Application;  
                return application;  
            }  
        }  
        public UIDocument UIDocument {  
            get {  
                if (uiDocument == null) uiDocument = UIApplication.ActiveUIDocument;  
                return uiDocument;  
            }  
        }  
        public Document Document {  
            get {  
                if (document == null) document = UIDocument.Document;  
                return document;  
            }  
        }  
        public Selection Selection {  
            get {  
                if (selection == null) selection = UIDocument.Selection;  
                return selection;  
            }  
        }  
        public Transaction Transaction {  
            get {  
                if (transaction == null) transaction = new Transaction(Document, "Add-in");  
                return transaction;  
            }  
        }  
        public View ActiveView {  
            get {  
                if (activeView == null) activeView = Document.ActiveView;  
                return activeView;  
            }  
        }  
        public List < Element > InstanceElements {  
                get {  
                    if (instanceElements == null) instanceElements = new FilteredElementCollector(Document).WhereElementIsNotElementType().ToList();  
                    return instanceElements;  
                }  
            } // thuộc tính khác...  
    }  
}

 

c.       Tạo class WPFData

Kiểu dữ liệu WPFData được định nghĩa để lưu trữ các dữ liệu được input bởi người sử dụng addin như đường kính thép, tầng bố trí, …

using Autodesk.Revit.DB;  
using Autodesk.Revit.DB.Structure;  
using System.ComponentModel;  
using System.Runtime.CompilerServices;  
namespace SingleData {  
    public class WPFData: INotifyPropertyChanged { // Định nghĩa các biến thành viên  
        private RebarBarType selectedRebarType;  
        private Level selectedLevel;  
        private int inputDiameter;  
        private double inputSpacing; // biến thành viên khác ... // Định nghĩa các thuộc tính để truy xuất  
        public RebarBarType SelectedRebarType {  
            get {  
                return selectedRebarType;  
            }  
            set {  
                selectedRebarType = value;  
                OnPropertyChanged();  
            }  
        }  
        public Level SelectedLevel {  
            get {  
                return selectedLevel;  
            }  
            set {  
                selectedLevel = value;  
                OnPropertyChanged();  
            }  
        }  
        public int InputDiameter {  
            get {  
                return inputDiameter;  
            }  
            set {  
                if (inputDiameter == value) return;  
                inputDiameter = value;  
                OnPropertyChanged();  
            }  
        }  
        public double InputSpacing {  
            get {  
                return inputSpacing;  
            }  
            set {  
                if (inputSpacing == value) return;  
                inputSpacing = value;  
                OnPropertyChanged();  
            }  
        } // các thuộc tính khác ... // Định nghĩa sự kiện thay đổi thuộc tính trong WPF  
        public event PropertyChangedEventHandler PropertyChanged;  
        private void OnPropertyChanged([CallerMemberName] string propertyName = null) {  
            PropertyChanged ? .Invoke(this, new PropertyChangedEventArgs(propertyName));  
        }  
    }  
}

 

d.      Tạo class OtherData

OtherData có tác dụng lưu trữ các kiễu dữ liệu tự định nghĩa hoặc dữ liệu thuộc loại khác.

namespace SingleData {  
    public class OtherData { // Định nghĩa các biến thành viên // kiểu dữ liệu của chúng có thể phân loại là kiểu người phát triển tự định nghĩa  
        private RebarInputForm rebarInputForm;  
        private RebarInfo rebarInfo;  
        private AssemblyInstanceInfo assemblyInstanceInfo; // biên1 thành viên khác ... // Định nghĩa các thuộc tính để truy xuất  
        public RebarInputForm RebarInputForm {  
            get {  
                if (rebarInputForm == null) rebarInputForm = new RebarInputForm();  
                return rebarInputForm;  
            }  
        }  
        public RebarInfo RebarInfo {  
            get {  
                if (rebarInfo == null) rebarInfo = new RebarInfo();  
                return rebarInfo;  
            }  
        }  
        public AssemblyInstanceInfo AssemblyInstanceInfo {  
            get {  
                if (assemblyInstanceInfo == null) assemblyInstanceInfo = new AssemblyInstanceInfo();  
                return assemblyInstanceInfo;  
            }  
        } // thuộc tính khác...  
    }  
}

 

e.      Khởi tạo thể hiện tĩnh, truy xuất và lưu trữ dữ liệu

Thực hiện tương tự như mục 2.c.

using Autodesk.Revit.Attributes;  
using Autodesk.Revit.DB;  
using Autodesk.Revit.UI;  
using SingleData;  
using System.Linq;  
namespace RevitAddin1 {  
    [Transaction(TransactionMode.Manual)] public class Command: IExternalCommand {  
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { // Khởi tạo thuộc tính tĩnh Instance đại diện cho vị trí lưu trữ dữ liệu  
            Singleton.Instance = new Singleton(); // Truy xuất thuộc tính UIApplication trong Singleton.RevitData và gán bằng dữ liệu lấy được trong Revit  
            Singleton.Instance.RevitData.UIApplication = commandData.Application; // Truy xuất thuộc tính RebarInputForm trong Singleton.OtherData và thực thi phương thức  
            Singleton.Instance.OtherData.RebarInputForm.ShowDialog(); // Truy xuất thuộc tính Transaction trong Singleton.RevitData và thực thi phương thức  
            Singleton.Instance.RevitData.Transaction.Start(); // Truy xuất thuộc tính Selection trong Singleton.RevitData và thực thi phương thi  
            Singleton.Instance.RevitData.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element); // Các truy xuất và xử lý khác ... // Truy xuất thuộc tính Transaction trong Singleton.RevitData và thực thi phương thức  
            Singleton.Instance.RevitData.Transaction.Commit();  
            return Result.Succeeded;  
        }  
    }  
}

4.      Đặc điểm của Singleton:

a.       Ưu điểm

Singleton giúp bạn quản lý dữ liệu dự án hiệu quả và thuận tiện khi tập trung các dữ liệu cần thiết về một cơ sở trung tâm. Nếu dự án lập trình của bạn phức tạp với nhiều class khác nhau, Singleton giúp truy xuất và xử lý dữ liệu dễ dàng.

using Autodesk.Revit.Attributes;  
using Autodesk.Revit.DB;  
using Autodesk.Revit.UI;  
using SingleData;  
using System.Linq;  
namespace RevitAddin1 {  
    [Transaction(TransactionMode.Manual)] public class Command1: IExternalCommand {  
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { // Khởi tạo thuộc tính tĩnh Instance đại diện cho vị trí lưu trữ dữ liệu  
            Singleton.Instance = new Singleton(); // Truy xuất thuộc tính UIApplication trong Singleton.RevitData và gán bằng dữ liệu lấy được trong Revit  
            Singleton.Instance.RevitData.UIApplication = commandData.Application; // Truy xuất thuộc tính RebarInputForm trong Singleton.OtherData và thực thi phương thức  
            Singleton.Instance.OtherData.RebarInputForm.ShowDialog(); // Các truy xuất và xử lý khác ...  
            return Result.Succeeded;  
        }  
        public Element GetFirstElement() { // Truy xuất thuộc tính InstanceElement trong Singleton.RevitData  
            return Singleton.Instance.RevitData.InstanceElements.First();  
        }  
    }[Transaction(TransactionMode.Manual)] public class Command2: IExternalCommand {  
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { // Khởi tạo thuộc tính tĩnh Instance đại diện cho vị trí lưu trữ dữ liệu  
            Singleton.Instance = new Singleton(); // Truy xuất thuộc tính UIApplication trong Singleton.RevitData và gán bằng dữ liệu lấy được trong Revit  
            Singleton.Instance.RevitData.UIApplication = commandData.Application; // Truy xuất thuộc tính RebarInputForm trong Singleton.OtherData và thực thi phương thức  
            Singleton.Instance.OtherData.RebarInputForm.ShowDialog(); // Truy xuất thuộc tính SelectedLevel trong Singleton.WPFData  
            TaskDialog.Show("Revit", Singleton.Instance.WPFData.SelectedLevel.Name); // Các truy xuất và xử lý khác ...  
            return Result.Succeeded;  
        }  
    }  
}

Có thể tạo các file .cs định nghĩa Singleton để thiết lập tổ chức dữ liệu như nhau làm tăng năng suất phát triển ứng dụng.

b.      Nhược điểm

Mặc dù Singleton tạo một nơi để lưu trữ dữ liệu dài hạn, nhưng dữ liệu sẽ bị mất đi khi ứng dụng hoàn thành hoặc tắt đi). Do đó, Singleton không phải là một giải pháp lưu trữ dữ liệu dài hạn.

Khi nghĩ đến một giải pháp lưu trữ dữ liệu lâu dài, bạn nên cân nhắc lưu trữ thông tin trên file hoặc gửi dữ liệu về server…

Cố gắng sử dụng Singleton cho mọi thứ trong mọi phần của dự án kể cả chúng không cần thiết là dư thừa, tốn thời gian viết code và bộ nhớ máy tính khi thực thi. (ps: tốn thêm bộ nhớ não để nhớ nữa nhé, :3)

5.      Kết luận:

Singleton là một giải pháp lưu trữ tập trung hóa dữ liệu dự án, lưu trữ và truy xuất dữ liệu hiệu quả. Singleton giúp bạn lập trình, tổ chức dữ liệu có hệ thống khi dự án gồm nhiều class phức tạp.

Song Singleton cũng có nhữngmặt hạn chế của nó: không phải là giải pháp lưu trữ dữ liệu dài hạn, bạn cũng hạn chế gom mọi thứ vào quản lý bằng Singleton nếu dữ liệu không cần thiết.

Để sử dụng Singleton hiệu quả, bạn hãy cân nhắc các câu hỏi sau: Dữ liệu này có quan trọng? sử dụng ở phần nào của dự án? truy xuất và xử lý thế nào?

 

Bài viết liên quan