變異子與訪問子
在電腦科學中, 變異子方法(mutator或setter)是一種用於控制變數更改的方法。通常,變異子伴隨着訪問子 (accessor或getter)返回私有成員變數的值。
變異子方法最常用於物件導向的編程封裝,與封裝原則保持一致。根據這個原則,類的成員變數被設為私有以隱藏和保護它們不受其他代碼的影響,並且只能由公共成員函數(變異子方法)修改,該函數將所需的新值作為參數來修改私有成員變數。可以將變異子方法與賦值運算子多載進行比較。
變異子方法也可以在非物件導向的環境中使用。在這種情況下,對要修改的變數的參照與新值一起傳遞給變異子。編譯器無法限制代碼繞過變異子方法並直接更改變數。開發人員有責任確保僅通過變異子方法修改變數,而不是直接修改變數。
在支援它們的程式語言中,屬性提供了一種方便的替代方案,而不放棄封裝的效用。
在下面的範例中,完全實現的 變異子方法還可以驗證輸入數據或採取進一步的操作,例如觸發事件 。
影響
[編輯]定義變異子和 訪問子方法或屬性塊的另一種方法是為實例變數提供一些可見性,而不是私有化它或直接從對象外部訪問它。可以使用修改器和訪問器定義更精細的控制訪問權限。例如,將一個參數設為唯讀,可以通過定義一個訪問器而不提供修改器。兩種方法的可見性可能不同:訪問器通常是公開的;變異子是保護的、包私有的或內部的。
定義了變異子的塊有機會驗證或預處理傳入的數據。如果保證所有外部訪問都通過變異子,則無法繞過這些步驟。例如,如果日期由單獨的私有 year
、 month
和day
變數表示,則傳入日期由變異子setDate
拆分,同時為了一致性,相同的私有實例變數由setYear
和setMonth
訪問。在所有情況下,1 - 12 之外的月份值都可以被相同的代碼拒絕。
相反,訪問器允許從內部變數合成有用的數據表示,同時保持它們的結構封裝和對外部模組不可見。貨幣getAmount
訪問子可以由一個內部私有的數值變數構建一個字串,其小數碼數由參數currency
定義。
現代程式語言常提供在一行中修改器和訪問器的生成樣板—例如 C# 的 public string Name { get; set; }
和Ruby的 attr_accessor :name
。在這些情況下,不會為驗證、預處理或合成等操作建立代碼塊。這些簡化的訪問器仍然保留了對簡單公共實例變數進行封裝的優勢,但常見的是,隨着敏捷軟件開發、軟件維護和需求的變化,對數據的需求變得更加複雜。許多自動修改器和訪問器最終會被單獨的代碼塊取代。在實現的早期自動建立它們的好處是,無論是否添加了更複雜的類,類的公共介面都保持相同,如果添加,則不需要進行大量重構。 [1]
由於涉及額外的步驟,訪問器函數的效率可能低於直接取得或儲存數據欄位, [2]然而,這些函數通常是行內的,從而消除了函數呼叫的開銷。
例子
[編輯]匯編語言
[編輯]student struct
age dd ?
student ends
.code
student_get_age proc object:DWORD
mov ebx, object
mov eax, student.age[ebx]
ret
student_get_age endp
student_set_age proc object:DWORD, age:DWORD
mov ebx, object
mov eax, age
mov student.age[ebx], eax
ret
student_set_age endp
C
[編輯]檔案student.h:
#ifndef _STUDENT_H
#define _STUDENT_H
struct student; /* opaque structure */
typedef struct student student;
student *student_new(int age, char *name);
void student_delete(student *s);
void student_set_age(student *s, int age);
int student_get_age(student *s);
char *student_get_name(student *s);
#endif
檔案student.c:
#include <stdlib.h>
#include <string.h>
#include "student.h"
struct student {
int age;
char *name;
};
student *student_new(int age, char *name) {
student *s = malloc(sizeof(student));
s->name = strdup(name);
s->age = age;
return s;
}
void student_delete(student *s) {
free(s->name);
free(s);
}
void student_set_age(student *s, int age) {
s->age = age;
}
int student_get_age(student *s) {
return s->age;
}
char *student_get_name(student *s) {
return s->name;
}
檔案 main.c:
#include <stdio.h>
#include "student.h"
int main(void) {
student *s = student_new(19, "Maurice");
char *name = student_get_name(s);
int old_age = student_get_age(s);
printf("%s's old age = %i\n", name, old_age);
student_set_age(s, 21);
int new_age = student_get_age(s);
printf("%s's new age = %i\n", name, new_age);
student_delete(s);
return 0;
}
檔案 Makefile:
all: out.txt; cat $<
out.txt: main; ./$< > $@
main: main.o student.o
main.o student.o: student.h
clean: ;$(RM) *.o out.txt main
C++
[編輯]檔案Student.h:
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
class Student {
public:
Student(const std::string& name);
const std::string& name() const;
void name(const std::string& name);
private:
std::string name_;
};
#endif
檔案Student.cpp:
#include "Student.h"
Student::Student(const std::string& name) : name_(name) {
}
const std::string& Student::name() const {
return name_;
}
void Student::name(const std::string& name) {
name_ = name;
}
C#
[編輯]public class Student {
private string name;
/// <summary>
/// Gets or sets student's name
/// </summary>
public string Name {
get { return name; }
set { name = value; }
}
}
從(.NET Framework 3.5開始,可以縮寫為自動屬性:
public class Student {
public string Name { get; set; }
}
可以限制只有本類可以私有訪問:
public class Student {
public string Name { get; private set; }
}
Python
[編輯]下例子使用的類,與一個變數、一個訪問子,一個變異子。
class Student:
# Initializer
def __init__(self, name: str) -> None:
# An instance variable to hold the student's name
self._name = name
# Getter method
@property
def name(self):
return self._name
# Setter method
@name.setter
def name(self, new_name):
self._name = new_name
>>> bob = Student("Bob")
>>> bob.name
Bob
>>> bob.name = "Alice"
>>> bob.name
Alice
>>> bob._name = "Charlie" # bypass the setter
>>> bob._name # bypass the getter
Charlie
Visual Basic.NET
[編輯]類似於C#, 明確使用Get
與 Set
methods.
Public Class Student
Private _name As String
Public Property Name()
Get
Return _name
End Get
Set(ByVal value)
_name = value
End Set
End Property
End Class
VB.NET 2010, 利用Auto Implemented建立一個屬性而不必使用Get與Set語法。編譯器自動建立一個隱變數,如_name
對應於屬性名name
.
Public Class Student
Public Property name As String
End Class
參考文獻
[編輯]- ^ Stephen Fuqua. Automatic Properties in C# 3.0. 2009 [2009-10-19]. (原始內容存檔於2011-05-13).
- ^ Tim Lee. Run Time Efficiency of Accessor Functions. 1998-07-13 [2021-07-09]. (原始內容存檔於2016-03-05).