Программная интерпретация свойств

Как и для других элементов UML, интерпретировать свойства в про­грамме можно по-разному. Наиболее распространенным представле­нием является поле или свойство языка программирования. Так, класс Order Line (Строка заказа), показанный на рис. 3.1, мог бы быть представлен в Java следующим образом:

public class OrderLine...

private int quantity;

private Money price;

private Order order;

private Product product

В языке, подобном С#, который допускает свойства, это могло бы вы­глядеть так:

public class OrderLine...

public int Quantity;

public Money Price;

public Order Order;

public Product Product;

Обратите внимание, что атрибут обычно соответствует открытым (pub­lic) свойствам в языке, поддерживающем свойства, но соответствует закрытым (private) полям в языке, в котором такой поддержки нет. В языке без свойств с полями можно общаться посредством методов доступа (получение и установка). У атрибута только для чтения не бу­дет метода установки (в случае полей) или операции установки (в слу­чае свойства). Учтите, что если свойству не присвоить имя, то в общем случае ему будет назначено имя целевого класса.


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

public class OrderLine...

private int quantity;

private Product product;

public int getQuantity () {

return quantity;

}

public void setQuantityfint quantity) {

this.quantity = quantity;

}

public Money getPrice() {

return product.getPrice().multiply(quantity);

}

Здесь нет поля для цены - ее значение вычисляется. Но поскольку клиенты класса Order Line заинтересованы в этой информации, она выглядит как поле. Клиенты не могут сказать, что является полем, а что вычисляется. Такое сокрытие информации представляет сущ­ность инкапсуляции.

Если атрибут имеет несколько значений, то связанные с ним данные представляют собой коллекцию. Поэтому класс Order (Заказ) будет ссылаться на коллекцию классов Order Line. Поскольку эта кратность упорядочена (ordered), то и коллекция должна быть упорядочена (на­пример, List в Java или IList в.NET). Если коллекция не упорядочена, то, строго говоря, она не должна иметь ярко выраженного порядка, то есть должна быть представлена множеством, но большинство специа­листов реализуют неупорядоченные атрибуты также в виде списков. Некоторые разработчики используют массивы, но поскольку UML подразумевает неограниченность сверху, то я почти всегда для струк­туры данных применяю коллекцию.

Многозначные свойства имеют интерфейс, отличный от интерфейса свойств с одним значением (в Java):

class Order {

private Set lineltems = new HashSet();

public Set getLineltems() {

return Collections. unrnodifiableSet(llneltems);

}

public void addLineltem (Orderltem arg) {

lineltems,add (arg);

}

public void removeLineItem (Orderltem arg) {

lineltems.remove(arg);

}


В большинстве случаев значения многозначных свойств не присваива­ются прямо; вместо этого применяются методы добавления (add) и удаления (remove). Для того чтобы управлять своим свойством Line Items (Позиции заказов), заказ должен контролировать членство этой коллекции; поэтому он не должен передавать незащищенную коллек­цию. В таких случаях я использовал представителя защиты, чтобы за­ключить коллекцию в оболочку только для чтения. Можно также реа­лизовать необновляемый итератор или сделать копию. Конечно, так клиентам удобнее модифицировать объекты-члены, но они не должны иметь возможность напрямую изменять саму коллекцию.

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

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

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

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


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: