программирование, создание программ, учебник Delphi, учебник по программированию, бейсек, делфи, си, паскаль
 
 
 

 

Пакеты и интерфейсы


В стандартную библиотеку Java API входят сотни классов. Каждый программист в ходе работы добавляет к ним десятки своих. Множество классов становится необозримым. Уже давно принять классы объединять в библиотеки. Но библиотеки классов, кроме стандартной, не являются частью языка.
Разработчики Java включили в язык дополнительную конструкцию — паке-ты (packages). Все классы Java распределяются по пакетам. Кроме классов пакеты могут включать в себя интерфейсы и вложенные подпакеты (subpackages). Образуется древовидная структура пакетов и подпакедчэв.
Эта структура в точности отображается на структуру файловой системы. Все файлы с расширением class (содержащие байт-коды), образующие пакет, хранятся в одном каталоге файловой системы. Подпакеты собраны в подкаталоги этого каталога.
Каждый пакет образует одно пространство имен (namespace). Это означает, что все имена классов, интерфейсов и подпакетов в пакете должны быть уникальны. Имена в разных пакетах могут совпадать, но это будут разные программные единицы. Таким образом, ни один класс, интерфейс или под-пакет не может оказаться сразу в двух пакетах. Если надо использовать два класса с одинаковыми именами из разных пакетов, то имя класса уточняется именем пакета: пакет.класс . Такое уточненное имя называется полным именем класса (fully qualified name).
Все эти правила, опять-таки, совпадают с правилами хранения файлов и подкаталогов в каталогах.
Пакетами пользуются еще и для того, чтобы добавить к уже имеющимся правам доступа к членам класса private, protected и public еще один, "пакетный" уровень доступа.
Если член класса не отмечен ни одним из модификаторов private, protected, public, то, по умолчанию, к нему осуществляется пакетный доступ (default access), а именно, к такому члену может обратиться любой метод любого класса из того же пакета. Пакеты ограничивают и доступ к классу целиком — если класс не помечен модификатором public , то все его члены, даже открытые, public , не будут видны из других пакетов.
Как же создать пакет и разместить в нем классы и подпакеты?
Пакет и подпакет
Чтобы создать пакет надо просто в первой строке Java-файла с исходным кодом записать строку package имя; , например:
package mypack;
Тем самым создается пакет с указанным именем mypack и все классы, записанные в этом файле, попадут в пакет mypack . Повторяя эту строку в начале каждого исходного файла, включаем в пакет новые классы.
Имя подпакета уточняется именем пакета. Чтобы создать подпакет с именем, например, subpack , следует в первой строке исходного файла написать;
package mypack.subpack;
и все классы этого файла и всех файлов с такой же первой строкой попадут в подпакет subpack пакета mypack .
Можно создать и подпакет подпакета, написав что-нибудь вроде
package mypack.subpack.sub;
и т. д. сколько угодно раз.
Поскольку строка package имя; только одна и это обязательно первая строка файла, каждый класс попадает только в один пакет или подпакет.
Компилятор Java может сам создать каталог с тем же именем mypack, a в нем подкаталог subpack, и разместить в них class-файлы с байт-кодами.
Полные имена классов А, в будут выглядеть так: mypack.A, mypack.subpack.в.
Фирма SUN рекомендует записывать имена пакетов строчными буквами, тогда они не будут совпадать с именами классов, которые, по соглашению, начинаются с прописной. Кроме того, фирма SUN советует использовать в качестве имени пакета или подпакета доменное имя своего сайта, записанное в обратном порядке, например:
com.sun.developer
До сих пор мы ни разу не создавали пакет. Куда же попадали наши файлы с откомпилированными классами?
Компилятор всегда создает для таких классов безымянный пакет (unnamed package), которому соответствует текущий каталог (current working directory)
файловой системы. Вот поэтому у нас class-файл всегда оказывался в том же каталоге, что и соответствующий Java-файл.
Безымянный пакет служит обычно хранилищем небольших пробных или промежуточных классов. Большие проекты лучше хранить в пакетах. Например, библиотека классов Java 2 API хранится в пакетах java, javax, org.omg. Пакет Java содержит только подпакеты applet, awt, beans, io, lang, math, net, rmi, security, sql, text, util и ни одного класса. Эти пакеты имеют свои подпакеты, например, пакет создания ГИП и графики java.awt содержит подпакеты color, datatransfer, dnd, event, font, geometry, im,image, print.
Конечно, состав пакетов меняется от версии к версии.
Права доступа к членам класса
Пришло время подробно разобрать различные ограничения доступа к полям и методам класса.
Рассмотрим большой пример. Пусть имеется пять классов, размещенных в двух пакетах, как показано на  1.
В файле Base.java описаны три класса: inpi, Base и класс Derivedpi , расширяющий класс вазе. Эти классы размещены в пакете pi. В классе Base определены переменные всех четырех типов доступа, а в методах f() классов inp1 и Derivedp1 сделана попытка доступа ко всем полям класса вазе. Неудачные попытки отмечены комментариями. В комментариях помещены сообщения компилятора. Листинг 3.1 показывает содержимое этого файла.
Листинг 3.1. Файл Base.java с описанием пакета p1
package p1;
class Inp1{
  public void f () {
    Base b = new Base(); 
//    b.priv = 1;   // "priv has private access in p1.Base"
    b.pack = 1;
    b.prot = 1;
    b.publ = 1; 
  } 
}
public class Base{
  private int priv = 0; 
          int pack = 0;
protected int prot = 0;
   public int publ = 0; 
}
class Derivedpi extends Base{
  public void f(Base a) { 
// a.priv = 1; // "priv hds private access in pi.Base"
  a.pack = 1;
  a.prot = 1;
  a.publ = 1; 
//   priv = 1; // "priv has private access in pi.Base"
  pack = 1;
  prot = 1;
  publ = 1; 
  } 
}
Как видно из листинга 3.1, в пакете недоступны только закрытые, private , поля другого класса.
В файле Inp2.java описаны два класса: inp2 и класс Derivedp2 , расширяющий класс base . Эти классы находятся в другом пакете р2 . В этих классах тоже сделана попытка обращения к полям класса вазе. Неудачные попытки прокомментированы сообщениями компилятора. Листинг 3.2 показывает содержимое этого файла.
Напомним, что класс вазе должен быть помечен при своем описании в пакете p1 модификатором public , иначе из пакета р2 не будет видно ни одного его члена.
Листинг 3.2. Файл Inp2.java с описанием пакета р2
package p2; 
import pl.Base;
class Inp2{
  public static void main(String[] args){
    Base b = new Base();
// b.priv = 1;   // "priv has private access in pl.Base" 
// b.pack = 1;   // "pack is not public in pl.Base;
                 // cannot be accessed from outside package"
// b.prot = 1;   //„"prot has protected access in pi.Base"
b.publ = 1;
  }
}
class Derivedp2 extends Base{
public void, f (Base a){ 
// a.priv = 1;       // "priv has private access in. p1.Base" 
// a.pack = 1;       // "pack, is not public in pi.Base; cannot
                     //be accessed from outside package" 
// a.prot = 1;       // "prot has protected access in p1.Base"
 a.publ = 1;
// priv = 1;         // "priv has private access in pi.Base" 
// pack = 1;         // "pack is not public in pi.Base; cannot
                     // be accessed from outside package"
 prot = 1;
 publ = 1; 
 super.prot = 1; 
  } 
}
Здесь, в другом пакете, доступ ограничен в большей степени.
Из независимого класса можно обратиться только к открытым, public , полям класса другого пакета. Из подкласса можно обратиться еще и к защищенным, protected , полям, но только унаследованным непосредственно, а не через экземпляр суперкласса.
Все указанное относится не только к полям, но и к методам. Подытожим все сказанное в табл. 3.1.
Таблица 3.1. Права доступа к полям и методам класса

Класс

Пакет

Пакет и подклассы

Все классы

private

+

"package"

+

+

protected

 +

+

*

public

+

+

+

+

Особенность доступа к protected -полям и методам из чужого пакета отмечена звездочкой.
Размещение пакетов по файлам
То обстоятельство, что class-файлы, содержащие байт-коды классов, должны быть размещены по соответствующим каталогам, накладывает свои особенности на процесс компиляции и выполнения программы.
Обратимся к тому же примеру. Пусть в каталоге D:\jdkl.3\MyProgs\ch3 есть пустой подкаталог classes и два файла — Base.java и Inp2.java, — содержимое которых показано в листингах 3.1 и 3.2. Рис. 3.2 демонстрирует структуру каталогов уже после компиляции.
Мы можем проделать всю работу вручную.
1. В каталоге classes создаем подкаталоги р! и р2.
2. Переносим файл Base.java в каталог р! и делаем р] текущим каталогом.
3. Компилируем Base.java, получая в каталоге р! три файла: Base.class, Inpl.class, Derivedpl.class.
4. Переносим файл Inp2java в каталог р2.
5. Снова делаем текущим каталог classes.
6. Компилируем второй файл, указывая путь p2\Inp2.java.
7. Запускаем программу java p2.inp2.
Вместо шагов 2 и 3 можно просто создать три class-файла в любом месте, а потом перенести их в каталог pi. В class-файлах не хранится никакая информация о путях к файлам.
Смысл действий 5 и 6 в том, что при компиляции файла Inp2.java компилятор уже должен знать класс p1.Base , а отыскивает он файл с этим классом по пути p1.Base.class, начиная от текущего каталога.
Обратите внимание на то, что в последнем действии 7 надо указывать полное имя класса.
Если использовать ключи (options) командной строки компилятора, то можно выполнить всю работу быстрее.
1. Вызываем компилятор с ключом -d путь, указывая параметром путь начальный каталог для пакета:
javac -d classes Base.java
Компилятор создаст в каталоге classes подкаталог р1 и поместит туда три class-файла.
2. Вызываем компилятор с еще одним ключом -classpath путь, указывая параметром путь каталог classes, в котором находится подкаталог с уже откомпилированным пакетом pi:
javac -classpath classes -d classes Inp2.java
Компилятор, руководствуясь ключом -d, создаст в каталоге classes подкаталог р2 и поместит туда два class-файла, при создании которых он "заглядывал" в каталог pi, руководствуясь ключом -classpath.
3. Делаем текущим каталог classes.
4. Запускаем профамму java p2.inp2.
Для "юниксоидов" все это звучит, как музыка, ну а прочим придется вспомнить MS DOS.
Конечно, если вы используете для работы не компилятор командной строки, а какое-нибудь IDE, то все эти действия будут сделаны без вашего участия.
На  2 отображена структура каталогов после компиляции.
На  3 показан вывод этих действий в окно Command Prompt и содержимое каталогов после компиляции.
Импорт классов и пакетов
Внимательный читатель заметил во второй строке листинга 3.2 новый оператор import . Для чего он нужен?
Дело в том, что компилятор будет искать классы только в -одном пакете, именно, в том, что указан в первой строке файла. Для классов из другого пакета надо указывать полные имена. В нашем примере они короткие, и мы могли бы писать в листинге 3.2 вместо Base полное имя p1.Base.
Но если полные имена длинные, а используются классы часто, то стучать по клавишам, набирая полные имена, становится утомительно. Вот тут-то мы и пишем операторы import , указывая компилятору полные имена классов.
Правила использования оператора import очень просты: пишется слово import и, через пробел, полное имя класса, завершенное точкой с запятой. Сколько классов надо указать, столько операторов import и пишется.
Это тоже может стать утомительным и тогда используется вторая форма оператора import — указывается имя пакета или подпакета, а вместо короткого имени класса ставится звездочка *. Этой записью компилятору предписывается просмотреть весь пакет. В нашем примере можно было написать
import p1.*;
Напомним, что импортировать можно только открытые классы, помеченные модификатором public .
Внимательный читатель и тут настороже. Мы ведь пользовались методами классов стандартной библиотеки, не указывая ее пакетов? Да, правильно.
Пакет java.iang просматривается всегда, его необязательно импортировать. Остальные пакеты стандартной библиотеки надо указывать в операторах import , либо записывать полные имена классов.
Подчеркнем, что оператор import вводится только для удобства программистов и слово "импортировать" не означает никаких перемещений классов.
Знатокам C/C++
Оператор import не эквивалентен директиве препроцессора include — он не подключает никакие файлы.
Java-файлы
Теперь можно описать структуру исходного файла с текстом программы на языке Java.

  • В первой строке файла может быть необязательный оператор package . 
  • В следующих строках могут быть необязательные операторы import . 
  • Далее идут описания классов и интерфейсов. 

Еще два правила.

  • Среди классов файла может быть только один открытый public -класс.
  • Имя файла должно совпадать с именем открытого класса, если последний существует.

Отсюда следует, что, если в проекте есть несколько открытых классов, то они должны находиться в разных файлах.
Соглашение "Code Conventions" рекомендует открытый класс, который, если он имеется в файле, нужно описывать первым.
Интерфейсы
Вы уже заметили, что получить расширение можно только от одного класса, каждый класс в или с происходит из неполной семьи, как показано на  4, а. Все классы происходят только от "Адама", от класса object . Но часто возникает необходимость породить класс о от двух классов вис, как показано на  4, б. Это называется множественным наследованием (multiple inheritance). В множественном наследовании нет ничего плохого. Трудности возникают, если классы вис сами порождены от одного класса А, как показано на  4* в. Это так называемое "ромбовидное" наследование.
В самом деле, пусть в классе А определен метод f (), к которому мы обращаемся из некоего метода класса о. Можем мы быть уверены, что метод f о выполняет то, что написано в классе А, т. е. это метод A.f о? Может, он переопределен в классах в и с? Если так, то каким вариантом мы пользуемся: B.f() или c.f()? Конечно, можно определить экземпляры классов и обращаться к методам этих экземпляров, но это совсем другой разговор.
В разных языках программирования этот вопрос решается по-разному, главным образом, уточнением имени метода ft). Но при этом всегда нарушается принцип KISS. Вокруг множественного наследования всегда много споров, есть его ярые приверженцы и столь же ярые противники. Не будем встревать в эти споры, наше дело — наилучшим образом использовать средства языка для решения своих задач.
Создатели языка Java после долгих споров и размышлений поступили радикально — запретили множественное наследование вообще. При расширении класса после слова extends можно написать только одно имя суперкласса. С помощью уточнения super можно обратиться только к членам непосредственного суперкласса.
Но что делать, если все-таки при порождении надо использовать несколько предков? Например, у нас есть общий класс автомобилей Automobile , от которого можно породить класс грузовиков Truck и класс легковых автомобилей Саг. Но вот надо описать пикап Pickup . Этот класс должен наследовать свойства и грузовых, и легковых автомобилей.
В таких случаях используется еще одна конструкция языка Java— интерфейс. Внимательно проанализировав ромбовидное наследование, теоретики ООП выяснили, что проблему создает только реализация методов, а не их описание.
Интерфейс (interface), в отличие от класса, содержит только константы  и заголовки методов, без их реализации.
Интерфейсы размещаются в тех же пакетах и подпакетах, что и классы, и компилируются тоже в class-файлы.
Описание интерфейса начинается со слова interface , перед которым может стоять модификатор public , означающий, как и для класса, что интерфейс доступен всюду. Если же модификатора public нет, интерфейс будет виден только в своем пакете.
После слова interface записывается имя интерфейса, .потом может ;стоять слово extends и список интерфейсов-предков через запятую. Таким образом, интерфейсы могут порождаться от интерфейсов, образуя свою, независимую от классов, иерархию, причем в ней допускается множественное наследование интерфейсов. В этой иерархии нет корня, общего предка.
Затем, в фигурных скобках, записываются в любом порядке константы и заголовки методов. Можно сказать, что в интерфейсе все методы абстрактные, но слово abstract писать не надо. Константы всегда статические, но слова static и final указывать не нужно.
Все константы и методы в интерфейсах всегда открыты, не надо даже .указывать модификатор public .
Вот какую схему можно предложить для иерархии автомобилей:
interface Automobile{ . . . }
interface Car extends Automobile{ . . . }
interface Truck extends Automobile{ . . . } 
interface Pickup extends Car, Truck{ . . . }
Таким образом, интерфейс — это только набросок, эскиз. В нем указано, что делать, но не указано, как это делать.
Как же использовать интерфейс, если он полностью абстрактен, в нем нет ни одного полного метода?
Использовать нужно не интерфейс, а его реализацию (implementation). Реализация интерфейса — это класс, в котором расписываются методы одного или нескольких интерфейсов. В заголовке класса после его имени или после имени его суперкласса, если он есть, записывается слово implements и, через запятую, перечисляются имена интерфейсов.
Вот как можно реализовать иерархию автомобилей:
interface Automobile{ . . . }
interface Car extends Automobile! . . . }
class Truck implements Automobile! . . . }
class Pickup extends Truck implements Car{ . . . }
или так:
interface Automobile{ . . . } 
interface Car extends Automobile{ . . . } 
interface Truck extends Automobile{ . . . } 
class Pickup implements Car, Truck{ . . . }
Реализация интерфейса может быть неполной, некоторые методы интерфейса расписаны, а другие — нет. Такая реализация — абстрактный класс, его обязательно надо пометить модификатором abstract .
Как реализовать в классе pickup метод f() , описанный и в интерфейсе саг, и в интерфейсе Truck с одинаковой сигнатурой? Ответ простой — никак. Такую ситуацию нельзя реализовать в классе Pickup . Программу надо спроектировать по-другому.
Итак, интерфейсы позволяют реализовать средствами Java чистое объектно-ориентированное проектирование, не отвлекаясь на вопросы реализации проекта.
Мы можем, приступая к разработке проекта, записать его в виде иерархии интерфейсов, не думая о реализации, а затем построить по этому проекту иерархию классов, учитывая ограничения одиночного наследования и видимости членов классов.
Интересно то, что мы можем создавать ссылки на интерфейсы. Конечно, указывать такая ссылка может только на какую-нибудь реализацию интерфейса. Тем самым мы получаем еще один способ организации полиморфизма.
Листинг 3.3 показывает, как можно собрать с помощью интерфейса хор домашних животных из листинга 2.2.
Листинг 3.3. Использование интерфейса для организации полиморфизма
interface Voice{
void voice(); 
}
class Dog implements Voice{
  public void voice (){
    System.out.println("Gav-gav!");
  } 
}
class Cat implements Voice{
  public void voice (){
    System.out.println("Miaou!");
  } 
}
class Cow implements Voice{ 
  public void voice(){
    System.out.println("Mu-u-u!"); 
  }
}
public class Chorus{
  public static void main(String[] args){ 
    Voiced singer = new Voice[3]; 
    singer[0] = new Dog(); 
    singer[1] = new Cat(); 
    singer[2] = new Cow(); 
    for(int i = 0; i < singer.length; i++)
      singer[i].voice();
  }
}
Здесь используется интерфейс voice вместо абстрактного класса Pet , описанного в листинге 2.2.
Что же лучше использовать: абстрактный класс или интерфейс? На этот вопрос нет однозначного ответа.
Создавая абстрактный класс, вы волей-неволей погружаете его в иерархию классов, связанную условиями одиночного наследования и единым предком — классом object . Пользуясь интерфейсами, вы можете свободно проектировать систему, не задумываясь об этих ограничениях.
С другой стороны, в абстрактных классах можно сразу реализовать часть методов. Реализуя же интерфейсы, вы обречены на скучное переопределение всех методов.
Вы, наверное, заметили и еще одно ограничение: все реализации методов интерфейсов должны быть открытыми, public , поскольку при переопределении можно лишь расширять доступ, а методы интерфейсов всегда открыты.
Вообще же наличие и классов, и интерфейсов дает разработчику богатые возможности проектирования. В нашем примере, вы можете включить в хор любой класс, просто реализовав в нем интерфейс voice .
Наконец, можно использовать интерфейсы просто для определения констант, как показано в листинге 3.4.
Листинг 3.4. Система управления светофором
interface Lights{
  int RED    = 0;
  int YELLOW = 1;
  int GREEN  = 2;
  int ERROR  = -1; 

class Timer implements Lights{
  private int delay;
  private static int light = RED;
  Timer(int sec)(delay = 1000 * sec;}
  public int shift(){
    int count = (light++) % 3; 
    try{
      switch(count){
        case RED: Thread.sleep(delay); break; 
        case YELLOW: Thread.sleep(delay/3); break; 
        case GREEN: Thread.sleep(delay/2); break; 
      }
    }catch(Exception e){return ERROR;} 
    return count;
  }
}
class TrafficRegulator{
  private static Timer t = new Timer(1);
  public static void main(String[] args){
    for (int k = -0; k < 10; k++)
      switch(t.shift()){
      case Lights.RED:    System.out.println("Stop!"); break; 
      case Lights.YELLOW: System.out.println("Wait!"); break; 
      case Lights.GREEN:  System.out.println("Go!");   break; 
      case Lights.ERROR:  System.err.println("Time Error"); break; 
      default: System.err.println("Unknown light."); return;
    }
  }
}
Здесь, в интерфейсе Lights , определены константы, общие для всего проекта.
Класс Timer реализует этот интерфейс и использует константы напрямую как свои собственные. Метод shift о этого класса подает сигналы переключения светофору с разной задержкой в зависимости от цвета. Задержку осуществляет метод sleep() класса Thread из стандартной библиотеки, которому передается время задержки в миллисекундах. Этот метод нуждается в обработке исключений try{} catch() {} , о которой мы будем говорить в главе 16.
Класс TrafficReguiator не реализует интерфейс Lights и пользуется полными именами Lights.RED и т.д. Это возможно потому, что константы RED, YELLOW и GREEN по умолчанию являются статическими.
Теперь нам известны все средства языка Java, позволяющие проектировать решение поставленной задачи. Заканчивая разговор о проектировании, нельзя не упомянуть о постоянно пополняемой коллекции образцов проектирования (design patterns).
Design patterns
В математике давно выработаны общие методы решения типовых задач. Доказательство теоремы начинается со слов: "Проведем доказательство от противного" или: "Докажем это методом математической индукции", и вы сразу представляете себе схему доказательства, его путь становится вам понятен.
Нет ли подобных общих методов в программировании? Есть.
Допустим, вам поручили автоматизировать метеорологическую станцию. Информация от различных датчиков или, другими словами, контроллеров температуры, давления, влажности, скорости ветра поступает в цифровом виде в компьютер. Там она обрабатывается: вычисляются усредненные значения по регионам, на основе многодневных наблюдений делается прогноз на завтра, т. е. создается модель метеорологической картины местности. Затем прогноз выводится по разным каналам: на экран монитора, самописец, передается по сети. Он представляется в разных видах, колонках чисел, графиках, диаграммах.
Естественно спроектировать такую автоматизированную систему из трех частей.

  • Первая часть, назовем ее Контроллером (controller), принимает сведения от датчиков и преобразует их в какую-то единообразную форму, пригодную для дальнейшей обработки, например, приводит к одному масштабу. При этом для каждого датчика надо написать свой модуль, на вход которого поступают сигналы конкретного устройства, а на выходе образуется унифицированная информация.
  •  Вторая часть, назовем ее Моделью (model), принимает эту унифицированную информацию от Контроллера, ничего не зная о датчике и не интересуясь тем, от какого именно датчика она поступила, и преобразует ее по своим алгоритмам опять-таки к какому-то однообразному виду, например, к последовательности чисел.
  • Третья часть системы, Вид (view), непосредственно связана с устройствами вывода и преобразует поступившую от Модели последовательность чисел в график, диаграмму или пакет для отправки по сети. Для каждого устройства придется написать свой модуль, учитывающий особенности именно этого устройства.

В чем удобство такой трехзвенной схемы? Она очень гибка. Замена одного датчика приведет к замене только одного модуля в Контроллере, ни Модель, ни Вид этого даже не заметят. Надо представить прогноз в каком-то новом виде, например, для телевидения? Пожалуйста, достаточно написать один модуль и вставить его в Вид. Изменился алгоритм обработки данных? Меняем Модель.
Эта схема разработана еще в 80-х годах прошлого столетия [То есть XX века. — Ред. ] в языке Smalltalk и получила название MVG (Model-View-Controller). Оказалось, что она применима во многих областях, далеких от метеорологии, всюду, где удобно отделить обработку от ввода и вывода информации.
Сбор информации часто организуется так. На экране дисплея открывается поле ввода, в которое вы набиваете сведения, допустим, фамилии в произвольном порядке, а в соседнем поле вывода отображается обработанная информация, например, список фамилий по алфавиту. Будьте уверены, что эта программа организована по схеме МУС. Контроллером служит поле ввода, Видом — поле вывода, а Моделью — метод сортировки фамилий. В третьей части книги мы рассмотрим примеры реализации этой схемы.
К середине 90-х годов накопилось много подобных схем. В них сконцентрирован многолетний опыт тысяч программистов, выражены наилучшие решения типовых задач.
Вот, пожалуй, самая простая из этих схем. Надо написать класс, у которого можно создать только один экземпляр, но этим экземпляром должны пользоваться объекты других классов. Для решения этой задачи предложена схема Singleton, представленная в листинге 3.5.
Листинг 3.5. Схема Singleton
final class Singleton{
  private static Singleton s = new Singleton(0); 
  private int k;
  private Singleton(int i){k = i;} 
  public static Singleton getReference()(return s;} 
  public int getValue(){return k;} 
  public void setValue(int i){k = i;} 

public class SingletonTest {
  public static void main(String[] args){ 
    Singleton ref = Singleton.getReference(); 
    System.out.println(ref.getValue()); 
    ref.setValue(ref.getValue() + 5); 
    System.out.println(ref.getValue()); 
  } 
}
Класс singleton окончательный — его нельзя расширить. Его конструктор закрытый — никакой метод не может создать экземпляр этого класса. Единственный экземпляр s класса singleton — статический, он создается внутри класса. Зато любой объект может получить ссылку на экземпляр методом getReference () , Изменить состояние экземпляра s методом s etValue() или просмотреть его текущее состояние методом getValue() .
Это только схема — класс singleton надо еще наполнить полезным содержимым, но идея выражена ясно и полностью.
Схемы проектирования были систематизированы и изложены в книге [7]. Четыре автора этой книги были прозваны "бандой четырех" (Gang of Four), а книга, коротко, "GoF". Схемы обработки информации получили название "Design Patterns". Русский термин еще не устоялся. Говорят о "шаблонах", "схемах разработки", "шаблонах проектирования".
В книге GoF описаны 23 шаблона, разбитые на три группы:
1. Шаблоны создания объектов: Factory, Abstract Factory, Singleton, Builder, Prototype.
2. Шаблоны структуры объектов: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy.
3. Шаблоны поведения объектов: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template, Visitor.
Описания даны, в основном, на языке C++. В книге [8] те же шаблоны представлены на языке Java. Той же теме посвящено электронное издание [9]. В книге [10] подробно обсуждаются вопросы разработки систем на основе design patterns.
Мы, к сожалению, не можем разобрать подробно design patterns в этой кни-те. Но каждый программист начала XXI века должен их знать. Описание многих разработок начинается словами: "Проект решен на основе шаблона", и структура проекта сразу становится ясна для всякого, знакомого с design patterns.
По ходу книги мы будем указывать, на основе какого шаблона сделана та или иная разработка.
Заключение
Вот мы и закончили первую часть книги. Теперь вы знаете все основные конструкции языка Java, позволяющие спроектировать и реализовать проект любой сложности на основе ООП. Оставшиеся конструкции языка, не менее важные, но реже используемые, отложим до четвертой части. Вторую и третью часть книги посвятим изучению классов и методов, входящих в Core API. Это будет для вас хорошей тренировкой.
Язык Java, как и все современные языки программирования, — это не только синтаксические конструкции, но и богатая библиотека классов. Знание этих классов и умение пользоваться ими как раз и определяет программиста-практика.

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика