js-OOP: Thừa kế – Cài đặt lớp dẫn xuất với javascript – Part 6

js-OOP: Thừa kế – Cài đặt lớp dẫn xuất với javascript – Part 6

Trong bài trước, tôi đã đưa ra mẫu đầy đủ cho việc cài đặt một class. Bây giờ bạn tự hỏi liệu với javascript và class vừa mới xây dựng, ta có thể tạo được lớp dẫn xuất của nó không. Trong bài viết này tôi sẽ trình bày cách để ta có thể cài đặt được nó.

Tôi sẽ trình bày các kỹ thuật để thực hiện được 3 việc sau:

  • Thừa kế toàn bộ các phương thức, thuộc tính, biến public của lớp cha
  • Thực hiện overriding các phương thức public
  • Cho phép gọi được các hàm của lớp cha trong các hàm lớp con (dạng base.methodInSuperClass())

Tính thừa kế

Tôi sử dụng tính chất sau của javascript để tạo ra tính thừa kế:

function A(){
	this.x = 10;
}
var a = new A();
alert(a.x); // 10
function A(){
	this.x = 10;
	var a1 = {x: 5};
	return a1;
}
var a = new A();
alert(a.x); // 5

Sự khác biệt ở đây là khi sử dụng từ khóa new để khởi tạo một object, nếu hàm khởi tạo không trả về dối tượng nào thì this sẽ được trả về, trái lại, đối tượng được return sẽ là kết quả của phép new (các dữ diệu trong this sẽ được giải phóng, mất tham chiếu).

Lợi dụng tính chất trên, tôi sẽ tạo thể hiện của lớp cha, định nghĩa thêm các thuộc tính, phương thức của lớp con gắn vào lớp cha để tạo thành thể hiện của lớp con, cuối cùng tôi trả về thể hiện này sẽ bao gồm đầy đủ các thuộc tính, phương thức của lớp cha cộng với các thuộc tính, phương thức mới của lớp con.

function SubClass(){
	var _this = new SuperClass();
	_this.newProperty = 0;
	_this.newMethod = function(){ /*...*/ }
	return _this;
}

Overriding các phương thức public

Để overriding một public method, đơn gian ta hãy định nghĩa lại nó mà thôi. Ví dụ SuperClass có medthod1 và ta muốn override nó tại SubClass, ta thực hiện như sau:

function SupperClass(){
	this.method1 = function() { alert("Method1"); }
	this.method2 = function() { this.method1(); }
}
function SubClass(){
	var _this = new SuperClass();
	_this.method1 = function() { alert("Overriding method1!"); }
	return _this;
}

var x = new SupperClass();
x.method1(); // Method1
x.method2(); // Method1

var y = new SubClass();
y.method1(); // Overriding method1
y.method2(); // Overriding method1

Ở ví dụ trên, tính đa hình (polymorphic) đã được thể hiện khi gọi method2.

Cho phép giữ tham chiếu và triệu gọi các phương thức thuộc lớp cha từ lớp con

Kỹ thuật này cho phép một phương thức ở lớp con gọi chính xác được một phương thức được định nghĩa ở lớp cha thay vì gọi hàm override của nó ở lớp con.
Ta thực hiện bằng cách lưu lại tham chiếu tới tất cả các phương thức, thuộc tính, biến public của lớp cha vào một đối tượng _base (_base sẽ là một biến private của lớp con), khi đó nếu muốn triệu gọi chính xác các thành phần lớp cha thì chỉ việc truy cập vào đối tượng _base này.
Sử dụng (for … in …) để thực hiện kỹ thuật copy toàn bộ tham chiếu trên.

function SubClass(){
	var _this = new SuperClass();
	var _base = {};
	for (var m in _this) _base[m] = _this[m];

	_this.method2 = function(){ _base.method1(); }
	return _this;
}

Việc lưu trữ các tham chiếu vào _base thực chất để nhỡ có cài đè hàm (override) thì ta vẫn giữ được tham chiếu đến hàm ở lớp cha để sau muốn thì triệu gọi được.

Mẫu cài đặt của lớp dẫn xuất

Dựa trên các kỹ thuật trên, tôi đưa ra mẫu cài đặt lớp dẫn xuất như sau:

(function () {
	/// kiểm tra tồn tại của BaseClass
	if (typeof (vn.eten.tek.PublicClass) === 'undefined') 
		throw new Error('vn.eten.tek.SubClass require vn.eten.tek.PublicClass!');

	/// kiểm tra các REFERENCES
	if (typeof (Library_Or_Class) === 'undefined') 
		throw new Error('Lỗi khi không thấy có thư viện hoặc class cần khác!');


	/// Định nghĩa NAMESPACE
	window.vn = window.vn || {};
	vn.eten = vn.eten || {};
	vn.eten.tek = vn.eten.tek || {};

	/// Định nghĩa Private Static Variable
	var privateStaticProperty = 0;

	/// Định nghĩa Private Static Property
	function privateStaticProperty (value) {
		if (typeof (value) === 'undefined') return privateStaticProperty;
		// validate value nếu cần
		privateStaticProperty = value;
	};

	/// Định nghĩa Private Static Method
	function privateStaticMethod() {
		// định nghĩa privateStaticMethod
	}

	/// Định nghĩa Public CLASS và khai báo constructor
	vn.eten.tek.SubClass = function (params) {
		var _this = new vn.eten.tek.PublicClass(parameters_for_PublicClass);
		var _base = {};
		for (var m in _this) _base[m] = _this[m];
		
		/// Định nghĩa Private Variable
		var privateVariable;

		/// Định nghĩa Private Property
		function privateProperty(value) {
			if (typeof (value) === 'undefined') return privateVariable;
			// validate value nếu cần
			privateVariable = value;
		};

		/// Định nghĩa Private Method
		function privateMethod() {
			// định nghĩa privateMethod
		}

		/// Định nghĩa Public Property
		_this.publicProperty = function (value) {
			if (typeof (value) === 'undefined') return privateVariable;
			// validate value nếu cần
			privateVariable = value;
		};

		/// Định nghĩa Public Method
		_this.publicMethod = function () {
			// định nghĩa publicMethod
		};
		
		/// Định nghĩa Public Overriding Property
		_this.publicOverridingProperty = function (value) {
			// định nghĩa publicOverridingProperty
		};

		/// Định nghĩa Public Overriding Method
		_this.publicOverridingMethod = function () {
			// định nghĩa publicOverridingMethod
		};

		/// Định nghĩa Public CONSTRUCTION
		(function () {
			// sử dụng _this như this
		})();
		
		return _this;
	};

	/// Định nghĩa Public Static Variable
	vn.eten.tek.SubClass.publicStaticVariable = "";

	vn.eten.tek.SubClass.publicStaticProperty = function (value) {
		if (typeof (value) === 'undefined') return privateStaticProperty;
		// validate value nếu cần
		privateStaticProperty = value;
	};

	/// Định nghĩa Public Static Method
	vn.eten.tek.SubClass.publicStaticMethod = function () {
		// định nghĩa publicStaticMethod
	};
})();

Ví dụ

Lấy ví dụ với lớp Hình hộp chữ nhật (vn.eten.tek.Rectangular) được định nghĩa ở part5, tôi sẽ định nghĩa lớp dẫn xuất của nó là Hình lập phương (vn.eten.tek.Cube) với hàm khởi tạo nhận một tham số, thêm thuộc tính kích thước (size):

(function () {
    /// kiểm tra tồn tại của base class
    if (typeof (vn.eten.tek.Rectangular) === 'undefined') 
		throw new Error('vn.eten.tek.Cube require vn.eten.tek.Rectangular!');

	/// kiểm tra các REFERENCES

    /// Định nghĩa NAMESPACE
    //  Nếu cùng namespace thì có thể không cần định nghĩa lại
	
    /// Định nghĩa Public Inheritance CLASS
    vn.eten.tek.Cube = function (size) {
        var _this = new vn.eten.tek.Rectangular(size, size, size);
		var _base = {};
		for(var m in _this) _base[m]=_this[m];
						
        /// Định nghĩa Private Variable
        var _size;

        /// Định nghĩa Public Property
        // override set/get
        _this.width = function (value) {
            return _this.size(value);
        };
        // override set/get
        _this.high = function (value) {
            return _this.size(value);
        };
        // override set/get
        _this.deep = function (value) {
            return _this.size(value);
        };

        // định nghĩa property mới
        _this.size = function (value) {
            if (typeof (value) === 'undefined') return _size;
			_base.width(value);
			_base.high(value);
			_base.deep(value);
            _size = value;
        };

        /// Định nghĩa Public Method
        // override method isCube
        _this.isCube = function () {
            return true;
        };

        /// Định nghĩa Public CONSTRUCTION
        (function () {
            
        })();
		
        return _this;
    };
})();

Các sử dụng, bạn phải import cả 2 class với thứ tự vn.eten.tek.Rectangular trước vn.eten.tek.Cube:

<script type="text/javascript" src="vn.eten.tek.Rectangular.js"></script>
<script type="text/javascript" src="vn.eten.tek.Cube.js"></script>

Kết quả sử dụng như sau:

alert("Số cạnh của hình hộp: " + vn.eten.tek.Rectangular.getEdge());
// Số cạnh của hình hộp: 12
var rec = new vn.eten.tek.Cube(1);
alert("Thể tích của hình hộp = " + rec.getVolume());
// Thể tích của hình hộp = 1
alert("Chiều rộng: " + rec.width());
// Chiều rộng: 1
alert("Hình hộp là lập phương: " + rec.isCube());
// Hình hộp là lập phương: true
rec.width(2);
alert("Chiều rộng sau khi tăng: " + rec.width());
// Chiều rộng sau khi tăng: 2
alert("Thể tích của hộp = " + rec.getVolume());
// Thể tích của hộp = 8
alert("Hình hộp là lập phương: " + rec.isCube());
// Hình hộp là lập phương: true
if (rec.isCube())
	alert("Kích thước của lập phương: " + rec.size());
	// Kích thước của lập phương: 2

Trong bài viết tới, tôi sẽ trình bày cách cài đặt một public static class và cài đặt design pattern singleton với một class trong javascript.

Khi trích dẫn bài viết từ tek.eten.vn, xin vui lòng ghi rõ nguồn. Chúng tôi sẽ rất cảm ơn bạn!