Часто в программе к некоторым переменным класса удобно выдать прямой доступ (только на чтение или и на чтение, и на запись). Если в языке нет механизма для автоматической генерации такого доступа, то приходится писать getter и setter методы, которые обеспечивают доступ к переменной.
public class Student {
private String name ;
public String getName() {
return name;
}
protected int age = 18;
public int getAge() {
return age;
}
protected CatOrDogName favoriteCatOrDog ;
public CatOrDogName getFavoriteCatOrDog() {
return favoriteCatOrDog;
}
public void setFavoriteCatOrDog(CatOrDogName newFavoriteCatOrDog) {
favoriteCatOrDog = newFavoriteCatOrDog;
}
}
Писать для всех переменных такие getter/setter методы может быть весьма утомительно. Предлагаю решение для автоматизации этого процесса при помощи макропроцессора m4.
Первое, что нужно сделать, — написать макрос для добавления префиксов к идентификатору. Поля и методы в данном случае именуются слитными фразами, первое слово пишется маленькими буквами, у каждого следующего заглавная только первая буква. Чтобы к такому идентификатору добавить префикс, нужно поднять первую букву и сложить две строки.
Объявим макрос для сложения двух его параметров:
define(`CONCAT', `$1$2')
Тут всё предельно просто — возвращаем первый и второй параметры, пристыкованные вместе. Теперь напишем макрос для поднятия всех символов в строке. Для этого я задействую макрос translit. В качестве первого параметра он принимает строку, в качестве второго параметра — набор символов, и в качестве третьего параметра — ещё один набор символов. Каждый встреченный в строке символ из первого набора будет заменён на соответствующий символ из второго набора. Наборы можно указывать разными способами, я не особо в них вникал, а воспользовался примерами из документации. Наш макрос будет выглядеть так:
define(`UPCASE', `translit($1, `a-z', `A-Z')')
Теперь напишем макрос, который поднимает только первый символ входящего параметра. Для этого задействуем ещё три макроса: len(s) возвращает длину переданной строки, substr(s, first, count) извлекает из строки фрагмент длиной count, начинающийся с символа номер first (нумерация символов с нуля), и, наконец, eval вычисляет арифметическое выражение.
define(`FIRSTUP', `CONCAT(UPCASE(substr($1,0,1)),
substr($1,1,eval(len($1) - 1)))')
Например, если теперь написать
FIRSTUP(veryLongName)
, то оно будет заменено на
VeryLongName
Осталось совсем немного для макроса, добавляющего префикс:
define(`ADDPREFIX', `CONCAT($1,FIRSTUP($2))')
Теперь перейдём к основной задаче. Я решил ввести следующие макросы:
PRIVATE(type, name, after) — поле приватное, для него определяется публичный геттер
PROTECTED(type, name, after) — доступ к полю наследуется, для него определяется публичный геттер
PUBLIC(type, name, after) — доступ к полю наследуется (но не публичен), для него определяются публичные геттер и сеттер.
Третий параметр after всех этих макросов нужен для написания явной инициализации полей. Реализация этих макросов технически проста:
define(`PRIVATE',
` private $1 $2 $3;
public $1 ADDPREFIX(`get', $2)() {
return $2;
}
')
define(`PROTECTED',
` protected $1 $2 $3;
public $1 ADDPREFIX(`get', $2)() {
return $2;
}
')
define(`PUBLIC',
` protected $1 $2 $3;
public $1 ADDPREFIX(`get', $2)() {
return $2;
}
public void ADDPREFIX(`set', $2)($1 ADDPREFIX(`new', $2)) {
$2 = ADDPREFIX(`new', $2);
}
')
Самый первый пример в этом посте теперь можно сгенерировать следующим кодом:
public class Student {
PRIVATE(String, name)
PROTECTED(int, age, = 18)
PUBLIC(CatOrDogName, favoriteCatOrDog)
}
Для красоты макросы PRIVATE, PROTECTED и PUBLIC я записал в несколько строк. Однако, на практике бывает полезно, чтобы компилятор выдавал строку с ошибкой в изначальном файле (а не в сгенерированном), для этого полезно записать определение этих макросов в одну строку.
Комментариев нет:
Отправить комментарий