апреля 10, 2011

m4: добавление префиксов и автогенерация setter/getter методов

Часто в программе к некоторым переменным класса удобно выдать прямой доступ (только на чтение или и на чтение, и на запись). Если в языке нет механизма для автоматической генерации такого доступа, то приходится писать 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 я записал в несколько строк. Однако, на практике бывает полезно, чтобы компилятор выдавал строку с ошибкой в изначальном файле (а не в сгенерированном), для этого полезно записать определение этих макросов в одну строку.


Комментариев нет:

Отправить комментарий

Постоянные читатели

Обо мне

Моя фотография
Мой e-mail: vitek_03(at)mail(dot)ru