русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Объекты с неопределенным состоянием


Дата добавления: 2015-06-12; просмотров: 739; Нарушение авторских прав


Если использовать для обозначения неопределенного состояния (то есть отсутствия) объекта встроенное значение null, то при каждом использовании ссылки придется проверять, не равна ли она null. Это быстро утомляет, а код получается излишне громоздким. Проблема заключается в том, что null не имеет собственного поведения, кроме выдачи NullPointerException при попытке выполнения с ним какой-либо операции. Иногда бывает полезно ввести понятие объекта с неопределенным состоянием, который принимает сообщения, но возвращает значение, свидетельствующее об отсутствии «настоящего» объекта. Таким образом, вы можете считать, что все объекты действительны, и вам не придется тратить время на проверки null (и читать полученный код). Было бы интересно представить себе язык программирования, автоматически создающий объекты с неопределенным состоянием, но на практике они применяются не так уж часто — иногда проверки null оказывается достаточно, иногда можно уверенно считать, что значение null вам не попадется, а иногда даже обработка аномальных ситуаций через NullPointerException является допустимой. Наибольшую пользу объекты с неопределенным состоянием приносят «вблизи от данных», представляя сущности в пространстве задачи. Простой пример: во многих системах имеется классPerson, а в коде возникают ситуации, когда объект не представляет конкретную личность (или, по крайней мере, информация о ней недоступна); при традиционном подходе вам следовало бы проверить ссылку null. Также можно воспользоваться объектом с неопределенным состоянием, но, даже несмотря на то, что такой объект будет отвечать на все сообщения, на которые отвечает «настоящий» объект, все равно потребуется способ проверки его на «определенность». Проще всего определить для этого специальный интерфейс:

//. net/mindview/uti1/Null.java



package net.mindview.util;

public interface Null {}

Это позволяет instanceof обнаруживать объекты с неопределенным состоянием и, что еще важнее, не требует включения метода isNull() во все классы (в конце концов, это фактически будет другим способом выполнения RTTI — так почему бы сразу не воспользоваться встроенными средствами?):

// typeinfo/Person.java

// Класс с неопределенным состоянием объекта

package typeinfo;

import net.mindview.util.*;

 

class Person {

 

public final String first;

public final String last;

public final String address; // И t д.

public static final Person NULL = new NullPerson();

 

public Person(String first, String last, String address){

this.first = first;

this.last = last;

this.address = address;

}

 

@Override

public String toString() {

return "Person: " + first + " " + last + " " + address;

}

 

public static class NullPerson extends Person implements Null {

private NullPerson() {

super("None", "None", "None");

}

@Override

public String toString() { return "NullPerson"; }

}

}

В общем случае объект с неопределенным состоянием является синглетным, поэтому он создается как экземпляр static final. Это возможно благодаря тому, что объект Person неизменяем — значения задаются в конструкторе, а затем чи­таются, но не могут изменяться (поскольку поля String по своей природе неизменяемы). Если вы захотите изменить NullPerson, его придется заменить новым объектом Person. Обратите внимание: для обнаружения обобщенной поддержки Null или более конкретного типа NullPerson можно использоватьinstanceof, но при синглетной архитектуре можно воспользоваться просто equals() или даже == для сравнения с Person.NULL. Представьте, что вы собираетесь открыть новое предприятие, но, пока вакансии еще не заполнены, в каждой должности Position можно временно хранить «заполнитель» — объект Person с неопределенным состоянием:

//• typeinfo/Position.java

package typeinfo;

 

class Position {

private String title;

private Person person;

public Position(String jobTitle, Person employee) {

title = jobTitle;

person = employee;

if(person == null)

person = Person.NULL;

}

public Position(String jobTitle) {

title = jobTitle;

person = Person.NULL;

 

}

public String getTitle() { return title; }

public void setTitle(String newTitle) { title = newTitle;}

public Person getPerson() { return person; }

public void setPerson(Person newPerson) {

person = newPerson;

if(person == null)

person = Person.NULL;

}

@Override

public String toString() {

return "Position: " + title + " " + person;

}

}

Превращать Position в объект с неопределенным состоянием не обязательно, потому что существование Person.NULL подразумевает неопределенность Position (возможно, позднее выяснится, что явная поддержка неопределенного состояния для Position нужна, и вы добавите ее, но в соответствии с одним из канонов экстремального программирования в начальный проект следует включить «простейшее решение, которое будет работать», и включать новые функции лишь по мере возникновения реальной необходимости). Теперь классStaff может проверять объекты с неопределенным состоянием при заполнении вакансий:

//: typeinfo/Staff.java

import java.util.*;

 

public class Staff extends ArrayList<Position> {

public void add(String title, Person person) {

add(new Position(title, person));

}

public void add(String... titles) {

for(String title : titles)

add(new Position(title));

}

public Staff(String... titles) { add(titles); }

public boolean positionAvailable(String title) {

for(Position position : this)

if(position.getTitle().equals(title) &&

position.getPerson() == Person.NULL)

return true;

return false;

}

public void fillPosition(String title, Person hire) {

for(Position position : this)

if(position.getTitle().equals(title) &&

position.getPerson() == Person.NULL) {

position.setPerson(hire);

return;

}

throw new RuntimeException(

"Position " + title + " not available");

}

public static void main(String[] args) {

Staff staff = new Staff("President", "CTO",

"Marketing Manager", "Product Manager",

"Project Lead", "Software Engineer",

"Software Engineer", "Software Engineer",

"Software Engineer", "Test Engineer",

"Technical Writer");

staff.fillPosition("President",

new Person("Me", "Last", "The Top, Lonely At"));

staff.fillPosition("Project Lead",

new Person("Janet", "Planner", "The Burbs"));

if(staff.positionAvailable("Software Engineer"))

staff.fillPosition("Software Engineer",

new Person("Bob", "Coder", "Bright Light City"));

System.out.println(staff);

}

}

<spoiler text="Output:">

[Position: President Person: Me Last The Top. Lonely At, Position. СТО NullPerson, Position:

Marketing Manager NullPerson. Position: Product Manager NullPerson, Position. Project Lead

Person: Janet Planner The Burbs. Position: Software Engineer Person: Bob Coder Bright Light City,

Position: Software Engineer NullPerson, Position: Software Engineer NullPerson. Position-

Software Engineer NullPerson. Position. Test Engineer NullPerson. Position: Technical Writer

NullPerson]

</spoiler> Обратите внимание: в некоторых местах нам по-прежнему приходится проверять объекты на определенное состояние, что принципиально не отличается от проверки null, но в других местах, скажем, при преобразованиях toString(), лишние проверки не нужны; мы просто считаем, что ссылка на объект действительна.

Если вместо конкретных классов используются интерфейсы, для автоматического создания объектов с неопределенным состоянием можно воспользоваться динамическим посредником. Допустим, имеется интерфейс Robot, определяющий имя и модель робота, а также список List<Operation>, определяющий, какие операции выполняет робот. Операция состоит из описания и команды:

//: typeinfo/Operation.java

 

public interface Operation {

String description();

void command();

}

Чтобы воспользоваться услугами робота, следует вызвать метод operations():

//: typeinfo/Robot.java

import java.util.*;

import net.mindview.util.*;

 

public interface Robot {

String name();

String model();

List<Operation> operations();

class Test {

public static void test(Robot r) {

if(r instanceof Null)

System.out.println("[Null Robot]");

System.out.println("Название: " + r.name());

System.out.println("Модель: " + r.model());

for(Operation operation : r.operations()) {

System.out.println(operation.description());

operation.command();

}

}

}

}

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

//: typeinfo/SnowRemovalRobot.java

import java.util.*;

 

public class SnowRemovalRobot implements Robot {

private String name;

public SnowRemovalRobot(String name) {this.name = name;}

public String name() { return name; }

public String model() { return "SnowBot Series 11"; }

public List<Operation> operations() {

return Arrays.asList(

new Operation() {

public String description() {

return name + " может убирать снег";

}

public void command() {

System.out.println(name + " убирает снег");

}

},

new Operation() {

public String description() {

return name + " может колоть лед";

}

public void command() {

System.out.println(name + " колет лед");

}

},

new Operation() {

public String description() {

return name + " может чистить крышу";

}

public void command() {

System.out.println(name + " чистит крышу");

}

}

);

}

public static void main(String[] args) {

Robot.Test.test(new SnowRemovalRobot("Slusher"));

}

}

<spoiler text="Output:">

Название: Slusher

Модель: SnowBot Series 11

Slusher может убирать снег

Slusher убирает снег

Slusher может колоть лед

Slusher колет лед

Slusher может чистить крышу

Slusher чистит крышу

</spoiler> Предполагается, что существуют разные типы роботов, и для каждого типа Robot объект с неопределенным состоянием должен делать что-то особенное — в нашем примере выдавать информацию о конкетном типе Robot, представленном объектом. Эта информация перехватывается динамическим посредником:

//: typeinfo/NullRobot.java

// Использование динамического посредника для создания

// объекта с неопределенным состоянием

import java.lang.reflect.*;

import java.util.*;

import net.mindview.util.*;

 

class NullRobotProxyHandler implements InvocationHandler {

private String nullName;

private Robot proxied = new NRobot();

NullRobotProxyHandler(Class<? extends Robot> type) {

nullName = type.getSimpleName() + " NullRobot";

}

private class NRobot implements Null, Robot {

public String name() { return nullName; }

public String model() { return nullName; }

public List<Operation> operations() {

return Collections.emptyList();

}

}

public Object

invoke(Object proxy, Method method, Object[] args)

throws Throwable {

return method.invoke(proxied, args);

}

}

 

public class NullRobot {

public static Robot

newNullRobot(Class<? extends Robot> type) {

return (Robot)Proxy.newProxyInstance(

NullRobot.class.getClassLoader(),

new Class[]{ Null.class, Robot.class },

new NullRobotProxyHandler(type));

}

public static void main(String[] args) {

Robot[] bots = {

new SnowRemovalRobot("SnowBee"),

newNullRobot(SnowRemovalRobot.class)

};

for(Robot bot : bots)

Robot.Test.test(bot);

}

}

<spoiler text="Output:">

Название: SnowBee Модель:

SnowBot Series 11

SnowBee может убирать снег

SnowBee убирает снег

SnowBee может колоть лед

SnowBee колет лед

SnowBee может чистить крышу

SnowBee чистит крышу

[Null Robot]

Название: SnowRemova1 Robot NullRobot

Модель: SnowRemovalRobot NullRobot

</spoiler> Каждый раз, когда вам требуется объект Robot с неопределенным состоянием, вы вызываете newNullRobot() и передаете тип Robot, для которого создается посредник. Посредник выполняет требования о поддержке интерфейсов Robot и Null, а также предоставляет имя опосредованного типа.



<== предыдущая лекция | следующая лекция ==>
Динамические посредники | Интерфейсы и информация о типах


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.167 сек.