Группа 10 назначена на курс Алгебра 2 страница

12. Система Аэрофлот. Администратор формирует летную Бригаду(пилоты, штурман, радист, стюардессы) на Рейс. Каждый Рейс выполняется Самолетом с определенной вместимостью и дальностью полета. Рейс может быть отменен из-за погодных условий в Аэропорту отлета или назначения. Аэропорт назначения может быть изменен в полете из-за технических неисправностей, о которых сообщил командир.

13. Система Периодические издания. Читатель может сделать Заявку, предварительно выбрав периодические Издания из списка. Система подсчитывает сумму для оплаты. Читатель оплачивает заявку. Администратор добавляет Заявку в «черный список», если Клиент не оплачивает её в определённый срок.

14. Система Заказ гостиницы. Клиент оставляет Заявку на Номер, указав количество мест в номере, класс апартаментов и время пребывания. Администратор рассматривает Заявку, подтверждает или отклоняет её. Результат просматривает Клиент. В случае подтверждения Заявки Клиент оплачивает услуги.

15. Система Жилищно-коммунальные услуги. Квартиросъемщик отправляет Заявку, в которой указывает род работ, масштаб и желаемое время выполнения. Диспетчер формирует соответствующую Бригаду и регистрирует её в Плане работ. Диспетчер может отклонить Заявку в случае занятости всех Бригад.

16. Система Прокат автомобилей. Клиент выбирает Автомобиль из списка доступных, заполняет форму Заказа, указывая паспортные данные, срок аренды. Администратор может отклонить Заявку, указав причины отказа. При подтверждении Заявки Клиент оплачивает Заказ. Система выписывает сумму. В случае повреждения Автомобиля Клиентом Администратор вносит соответствующие пометки.

Тестовые задания к главе 4

Вопрос 4.1.

Дан код:

class Base {}
class A extends Base {}
public class Quest{
public static void main(String[] args){
Base b = new Base();
A ob = (A) b;
} }

Результатом компиляции и запуска будет:

1) компиляция и выполнение без ошибок;

2) ошибка во время компиляции;

3) ошибка во время выполнения.

Вопрос 4.2.

Классы A и Quest2 находятся в одном файле. Что необходимо изменить в объявлении класса Quest2, чтобы оно было корректным?

public class A{}

class Quest2 extends A, Object {}

1) необходимо убрать спецификатор public перед A;

2) необходимо добавить спецификатор public к Quest2;

3) убрать после extends один из классов;

4) класс Object нельзя указывать явно.

Вопрос 4.3.

Дан код:

class A {A(int i) {}} // 1class B extends A {} // 2

Какие из следующих утверждений верны? (выберите два)

1) компилятор пытается создать по умолчанию конструктор для класса А;

2) компилятор пытается создать по умолчанию конструктор для класса В;

3) ошибка во время компиляции в строке 1;

4) ошибка во время компиляции в строке 2.

Вопрос 4.4.

Дан код, находящийся в файле Quest.java:

public class Base{
Base(){
int i = 1;
System.out.print(i);
} }
public class Quest4 extends Base{
static int i;
public static void main(String [] args){
Quest4 ob = new Quest4();
System.out.print(i);
} }

В результате компиляции и запуска будет выведено:

1) ошибка компиляции;

2) 0;

3) 10;

4) 1;

5) ошибка выполнения.

Вопрос 4.5.

Что будет результатом компиляции и выполнения следующего кода?

class Q {

private void show(int i){

System.out.println("1");

} }

class Quest5 extends Q{

public void show(int i){

System.out.println("2");

}

public static void main(String[] args){

Q ob = new Quest5();

int i = ‘1’; //1

ob.show(i);

} }

1) ошибка компиляции: метод show()недоступен;

2) ошибка времени выполнения: метод show()недоступен;

3) ошибка компиляции: несовпадение типов в строке 1;

4) 2;

5) 1.

Вопрос 4.6.

Что будет результатом компиляции и выполнения следующего кода?

class Q {

voidmQ(int i) {
System.out.print("mQ" + i);
} }
class Quest6 extends Q {
public void mQ(int i) {
System.out.print("mQuest" + i);
}
public void mP(int i) {
System.out.println("mP" + i);
}
public static void main(String args[]) {
Q ob = new Quest6(); //1
ob.mQ(1); //2
ob.mP(1); //3
} }

1) mQ1 mP1;

2) mQuest1 mP1;

3) ошибка компиляции в строке //1;

4) ошибка компиляции в строке //2;

5) ошибка компиляции в строке //3.

Вопрос 4.7.

Как следует вызвать конструктор класса A, чтобы в результате выполнения кода была выведена на консоль строка в “ Конструктор A ”.

class A{

A(int i){ System.out.print("Конструктор A"); }

}

public classQuest extends A{

public static void main(String[] args){

Quest s= new Quest();

//1

}

public Quest(){

//2

}

publicvoid show() {

//3

} }

1) вместо //1 написать A(1);

2) вместо //1 написать super(1);

3) вместо //2 написать super(1);

4) вместо //2 написать A(1);

5) вместо //3 написать super(1).

 
Глава 5

ПРОЕКТИРОВАНИЕ КЛАССОВ

Шаблоны проектирования GRASP

При создании классов, распределении обязанностей и способов взаимодействия объектов перед программистом возникает серьезная проблема моделирования взаимодействия классов и объектов. Представляются широкие возможности выбора. Неоптимальный выбор может сделать системы и их отдельные компоненты непригодными для поддержки, восприятия, повторного использования и расширения. Систематизация приемов программирования и принципов организации классов получила название шаблона.

Основные принципы объектно-ориентированного проектирования, применяемого при создании диаграммы классов и распределения обязанностей между ними, систематизированы в шаблонах GRASP(General Responsibility Assignment Software Patterns).

При этом были сформулированы основные принципы и общие стандартные решения, придерживаясь которых можно создавать хорошо структурированный
и понятный код.

Шаблон Expert

При проектировании классов на первом этапе необходимо определить общий принцип распределения обязанностей между классами проекта, а именно: в первую очередь определить кандидатов в информационные эксперты – классы, обладающие информацией, требуемой для выполнения своей функциональности.

Простые эксперты определять достаточно просто, но часто возникает необходимость дополнять класс атрибутами, и в этом случае необходимо сделать правильный выбор. Например, в подсистеме прохождения назначенного теста некоторому классу необходимо знать число вопросов, на которые получен ответ на текущий момент времени в процессе тестирования. Какой класс должен отвечать за знание количества вопросов, на которые дан ответ на текущий момент времени при прохождении теста, если определены следующие классы?

/*пример # 1 : шаблон Expert : LineRequestQuest.java :Test.java : Quest.java */

public class Test {//информационный эксперт

private intidTest;

private intnumberQuest;

private StringtestName;

// реализация конструкторов и методов

}

public class LineRequestQuest {

private intquestID;

// реализация конструкторов и методов

}

public class Quest {//информационный эксперт

private int idQuest;

private int testID;

// реализация конструкторов и методов

}

Необходимо узнать количество вопросов из теста, на которые дан ответ, то есть число созданных объектов классаLineRequestQuest. Такой информацией обладает лишь экземпляр объекта Test, так как этот класс ответствен за знание общего количества вопросов в тесте. Следовательно, с точки зрения шаблона Expert объект Test подходит для выполнения этой обязанности, т.е. является информационным экспертом.

/*пример # 2 : шаблон Expert : Test.java */

сlassTest {

private intidTest;

private intnumberQuest;

private StringtestName;

private intcurrentNumberQuest;

 

public intgetCurrentNumberQuest() {

// реализация

}

Test
-
currentNumberQuest:
-
idTest:
-
numberQuest:
-
testName:
+
getCurrentNumberQuest() : void
Quest
-
idQuest:
-
testID:
LineRequestQuest
-
questID:
1..n

// реализация конструкторов и методов

}

Рис. 5.1. Применение шаблона Expert

Преимущества следования шаблону Expert:

· сохранение инкапсуляции информации при назначении ответственности классам, которые уже обладают необходимой информацией для обеспечения своей функциональности;

· уклонение от новых зависимостей способствует обеспечению низкой степени связанности между классами (Low Coupling);

· добавление соответствующего метода способствует высокому зацеплению (Highly Cohesive) классов, если класс уже обладает информацией для обеспечения необходимой функциональности.

Однако назначение чрезмерно большого числа ответственностей классу при использовании шаблона Expert может привезти к получению слишком сложных классов, которые перестанут удовлетворять шаблонам Low Coupling и High Cohesion.

Шаблон Creator

Существует большая вероятность того, что класс проще, если он будет большую часть своего жизненного цикла ссылаться на создаваемые объекты.

После определения информационных экспертов следует определить классы, ответственные за создание нового экземпляра некоторого класса. Следует назначить классу B обязанность создавать экземпляры класса A, если выполняется одно из следующих условий:

· класс В агрегирует (aggregate) объекты А;

· класс В содержит (contains) объекты А;

· класс В записывает или активно использует (records or closely uses) экземпляры объектов А;

· классы B и A относятся к одному и тому же типу, и их экземпляры составляют, агрегируют, содержат или напрямую используют другие экземпляры того же класса;

· класс В содержит или получает данные инициализации (has the initializing data), которые будут передаваться объектам А при его создании.

Если выполняется одно из указанных условий, то класс В – создатель (creator) объектов А.

Инициализация объектов – стандартный процесс. Грамотное распределение обязанностей при проектировании позволяет создать слабо связанные независимые простые классы и компоненты.

В соответствии с шаблоном необходимо найти класс, который должен отвечать за создание нового экземпляра объекта Quest (агрегирующий экземпляры объектов Quest).

Поскольку объект LineRequestQuest использует объект Quest, согласно шаблону Creator он является кандидатом для выполнения обязанности, связанной с созданием экземпляров объектов Quest. В этом случае обязанности могут быть распределены следующим образом:

Рис. 5.2. Пример реализации шаблона Creator

/* пример # 3 : шаблон Creator: Qest.java: LineRequestQuest.java :Test.java */

public class Test {

private intidTest;

private intnumberQuest;

private StringtestName;

private intcurrentNumberQuest;

// реализация конструкторов и методов

}

public class LineRequestQuest {

private intquestID;

 

public void answerQuest() {

// реализация

Vector q = new Vector();

q.add(makeRequest(параметры));

//

}

publicQuest makeRequest(параметры) {

// реализация

return new Quest(параметры);

}

}

public class Quest{

private int idQuest;

private int testID;

publicQuest() {}

// реализация конструкторов и методов

}

Шаблон Creator способствует низкой зависимости между классами (Low Coupling), так как экземпляры класса, которым необходимо содержать ссылку на некоторые объекты, должны создавать эти объекты. При создании некоторого объекта самостоятельно класс тем самым перестает быть зависимым от класса, отвечающего за создание объектов для него. Распределение обязанностей выполняется в процессе создания диаграммы взаимодействия классов.

Шаблон Low Coupling

Степень связанности классов определяет, насколько класс связан с другими классами и какой информацией о других классах он обладает. При проектировании отношений между классами следует распределить обязанности таким образом, чтобы степень связанности оставалась низкой.

Наличие классов с высокой степенью связанности нежелательно, так как:

· изменения в связанных классах приводят к локальным изменениям в данном классе;

  • затрудняется понимание каждого класса в отдельности;
  • усложняется повторное использование, поскольку для этого требуется дополнительный анализ классов, с которыми связан данный класс.

Пусть требуется создать экземпляр класса Quest и связать его с объектом Test. В предметной области регистрация объекта Test выполняется объектом Course.

Рис. 5.3. Классы, которые необходимо связать

Далее экземпляр объекта Course должен передать сообщение makeQuest()объекту Test. Это значит, что в текущем тесте были получены идентификаторы всех вопросов, составляющих тест и становится возможным создание объектов типа Quest и наполнение собственно теста.

Рис. 5.4. Пример плохой реализации шаблона Low Coupling

/* пример # 4 : шаблон Low Coupling : Qest.java : Test.java : Course.java */

public class Course {

private int id;

privateString name;

 

public void makeTest(){

Test test = new Test(параметры);

// реализация

while(условие){

Quest quest = new Quest(параметры);

// реализация

test.addTest(quest);

}

// реализация

}

}

public class Test {

// поля

public void addTest(Quest quest){

// реализация

}

}

public class Quest {

// поля и методы

}

При таком распределении обязанностей предполагается, что класс Course связывается с классом Quest.

Второй вариант распределения обязанностей с уклонением класса Course от создания объектов вопросов представлен на рисунке 5.5.

Рис. 5.5. Пример правильной реализации шаблона Low Coupling

/* пример # 5 : шаблон Low Coupling: Qest.java: Test.java: Course.java */

public class Course {

private int id;

privateString name;

 

public void makeTest() {

Test test = new Test(параметры);

// реализация

test.addTest();

// реализация

}

}

public class Test {

// поля

public void addTest() {

// реализация

while(условие) {

Quest quest = new Quest(параметры);

// реализация

}

}

}

public class Quest {

// поля и методы

}

Какой из методов проектирования, основанный на распределении обязанностей, обеспечивает низкую степень связанности?

В обоих случаях предполагается, что объекту Test должно быть известно
о существовании объекта Quest.

При использовании первого способа, когда объект Quest создается объектом Course, между этими двумя объектами добавляется новая связь, тогда как второй способ степень связывания объектов не увеличивает. Более предпочтителен второй способ, так как он обеспечивает низкую связываемость.

В ООП имеются некоторые стандартные способы связывания объектов A и B:

· объект A содержит атрибут, который ссылается на экземпляр объекта B;

· объект Aсодержит метод, который ссылается на экземпляр объекта B, что подразумевает использование B в качестве типа параметра, локальной переменной или возвращаемого значения;

· класс объекта Aявляется подклассом объекта B;

· Bявляется интерфейсом, а класс объекта A реализует этот интерфейс.

Шаблон Low Coupling нельзя рассматривать изолированно от других шаблонов (Expert, Creator, High Cohesion). Не существует абсолютной меры для определения слишком высокой степени связывания.

Преимущества следования шаблону Low Coupling:

· изменение компонентов класса мало сказывается на других классах;

· принципы работы и функции компонентов можно понять, не изучая другие классы.

Шаблон High Cohesion

С помощью этого шаблона можно обеспечить возможность управления слож­ностью, распределив обязанности, поддерживая высокую степень зацепления.

Зацепление – мера сфокусированности класса. При высоком зацеплении обязанности класса тесно связаны между собой, и класс не выполняет работ непомерных объёмов. Класс с низкой степенью зацепления выполняет много разнородных действий или не связанных между собой обязанностей.

Возникают проблемы, связанные с тем, что класс:

· труден в понимании, так как необходимо уделять внимание несвязным (неродственным) идеям;

· сложен в поддержке и повторном использовании из-за того, что он должен быть использован вместе с зависимыми классами;

· ненадежен, постоянно подвержен изменениям.

Классы со слабым зацеплением выполняют обязанности, которые можно легко распределить между другими классами.

Пусть необходимо создать экземпляр класса Quest и связать его с заданным тестом. Какой класс должен выполнять эту обязанность? В предметной области сведения о вопросах на текущий момент времени при прохождении теста записываются в объекте Course, согласно шаблону для создания экземпляра объекта Quest можно использовать объект Course. Тогда экземпляр объекта Course сможет отправить сообщение makeTest() объекту Test. За прохождение теста отвечает объект Course, т.е. объект Course частично несет ответственность за выполнение операции makeTest(). Однако если и далее возлагать на класс Course обязанности по выполнению все новых функций, связанных с другими системными операциями, то этот класс будет слишком перегружен и будет обладать низкой степенью зацепления.

Этот шаблон необходимо применять при оценке эффективности каждого проектного решения.

Виды зацепления:

1. Очень слабое зацепление. Единоличное выполнение множества разнород­ных операций.

/*пример # 6 : очень слабое зацепление : Initializer.java */

public class Initializer {

public void createTCPServer(String port) {

// реализация

}

public int connectDataBase(URL url) {

// реализация

}

public void createXMLDocument(String name) {

// реализация

}

}

2. Слабое зацепление. Единоличное выполнение сложной задачи из одной функциональной области.

/*пример # 7 : слабое зацепление : NetLogicCreator.java */

public class NetLogicCreator {

public void createTCPServer() {

// реализация

}

public void createTCPClient() {

// реализация

}

public void createUDPServer() {

// реализация

}

public void createUDPClient() {

// реализация

}

}

3. Среднее зацепление. Несложные обязанности в нескольких различных областях, логически связанных с концепцией этого класса, но не связанных между собой.

/*пример # 8 : среднее зацепление : TCPServer.java */

public class TCPServer {

public void createTCPServer() {

// реализация

}

public void receiveData() {

// реализация

}

public void sendData() {

// реализация

}

public void compression() {

// реализация

}

public void decompression() {

// реализация

}

}

4. Высокое зацепление. Среднее количество обязанностей из одной функциональной области при взаимодействии с другими классами.

/*пример # 9 : высокое зацепление : TCPServerCreator.java : DataTransmission.java : CodingData.java */

public class TCPServerCreator {

public void createTCPServer() {

// реализация

}

}

public class DataTransmission {

public void receiveData() {

// реализация

}

public void sendData() {

// реализация

}

}

public class CodingData {

public void compression() {

// реализация

}

public void decompression() {

// реализация

}

}

Если обнаруживается, что используется слишком негибкий дизайн, который сложен в поддержке, следует обратить внимание на классы, которые не обла­дают свойством зацепления или зависят от других классов. Эти классы легки в узна­вании, поскольку они сильно взаимосвязаны с другими классами или содержат множество неродственных методов. Как правило, классы, которые не обладают сильной зависимостью с другими классами, обладают свойством зацепления и наоборот. При наличии таких классов необходимо реорганизовать их структуру таким образом, чтобы они по возможности не являлись зависимыми и обладали свойством зацепления.

Шаблон Controller

Одной из базовых задач при проектировании информационных систем является определение класса, отвечающего за обработку системных событий. При необходимости посылки внешнего события прямо объекту приложения, которое обрабатывает это событие, как минимум один из объектов должен содержать ссылку на другой объект, что может послужить причиной очень негибкого дизайна, если обработчик событий зависит от типа источника событий или источник событий зависит от типа обработчика событий.

В простейшем случае зависимость между внешним источником событий и внутренним обработчиком событий заключается исключительно в передаче событий. Довольно просто обеспечить необходимую степень независимости между источником событий и обработчиком событий, используя интерфейсы. Интерфейсов может оказаться недостаточно для обеспечения поведенческой независимости между источником и обработчиком событий, когда отношения между этими источником и обработчиком достаточно сложны.

Можно избежать зависимости между внешним источником событий и внут­ренним обработчиком событий путем введения между ними дополнительного объекта, который будет работать в качестве посредника при передаче событий. Этот объект должен быть способен справляться с любыми другими сложными аспектами взаимоотношений между объектами.

Согласно шаблону Controller, производится делегирование обязанностей по обработке системных сообщений классу, если он:

· представляет всю организацию или всю систему в целом (внешний контроллер);

· представляет активный объект из реального мира, который может участвовать в решении задачи (контроллер роли);

· представляет искусственный обработчик всех системных событий прецедента и называется ПрецедентHandler (контроллер прецедента) .

Для всех системных событий в рамках одного прецедента используется один и тот же контролер.

Controller – это класс, не относящийся к интерфейсу пользователя и отвечающий за обработку системных событий. Использование объекта-контроллера обеспечивает независимость между внешними источниками событий и внутренними обработчиками событий, их типом и поведением. Выбор определяется за-
цеплением и связыванием.