Дизайн със статични елементи

Въпреки че Java е обектно-ориентирана до голяма степен, тя не е чист обектно-ориентиран език. Една от причините Java да не е чисто обектно-ориентирана е, че не всичко в нея е обект. Например, Java позволява да декларират променливи от примитивни типове ( int, float, boolean, и т.н.), които не са обекти. А Java има статични полета и методи, които са независими и отделени от обектите. Тази статия дава съвети как да използвате статични полета и методи в Java програма, като същевременно поддържате обектно-ориентиран фокус във вашите проекти.

Времето на живот на клас във виртуална машина Java (JVM) има много прилики с времето на живот на обект. Точно както обектът може да има състояние, представено от стойностите на променливите на неговия екземпляр, клас може да има състояние, представено от стойностите на променливите на своя клас. Точно както JVM задава променливи на екземпляра на начални стойности по подразбиране преди изпълнение на кода за инициализация, JVM задава променливи на класа на начални стойности по подразбиране преди изпълнение на кода за инициализация. И подобно на обектите, класовете могат да бъдат събирани боклуци, ако към тях вече не се прави препратка от работещото приложение.

Въпреки това съществуват значителни разлики между класовете и обектите. Може би най-важната разлика е начинът, по който се извикват методите на instance и class: методите на instance (в по-голямата си част) са динамично обвързани, но методите на класа са статично обвързани. (В три специални случая методите на екземпляра не са динамично обвързани: извикване на методи на частен екземпляр, извикване на initметоди (конструктори) и извиквания с superключовата дума. Вижте Ресурси за повече информация.)

Друга разлика между класове и обекти е степента на скриване на данните, предоставена от нивата на частен достъп. Ако променлива на екземпляр е декларирана като частна, само методите на инстанцията могат да имат достъп до нея. Това ви позволява да осигурите целостта на данните на екземпляра и да направите обектите безопасни за нишките. Останалата част от програмата не може да осъществи директен достъп до тези променливи на екземпляра, но трябва да премине през методите на екземпляра, за да манипулира променливите на инстанцията. В опит да накарате един клас да се държи като добре проектиран обект, можете да направите променливите на класа частни и да дефинирате методите на класа, които ги манипулират. Независимо от това, вие не получавате толкова добра гаранция за безопасност на нишките или дори за целостта на данните по този начин, защото определен вид код има специална привилегия, която им дава директен достъп до променливи от частния клас: екземплярни методи,и дори инициализатори на променливи на екземпляра, могат да имат директен достъп до тези променливи на частния клас.

Така че статичните полета и методи на класовете, макар и сходни в много отношения с полетата на екземпляра и методите на обектите, имат значителни разлики, които трябва да повлияят на начина, по който ги използвате при проектирането.

Третиране на класовете като обекти

Докато проектирате Java програми, вероятно ще срещнете много ситуации, в които изпитвате нужда от обект, който действа по някакъв начин като клас. Може например да искате обект, чийто живот съвпада с този на клас. Или може да искате обект, който като клас се ограничава до един екземпляр в дадено пространство от имена.

В ситуации на проектиране като тези може да бъде изкушаващо да създадете клас и да го използвате като обект, за да дефинирате променливи на класа, да ги направите частни и да дефинирате някои публични методи на клас, които манипулират променливите на класа. Подобно на обект, такъв клас има състояние. Подобно на добре проектиран обект, променливите, които определят състоянието, са частни и външният свят може да повлияе на това състояние само чрез извикване на методите на класа.

За съжаление съществуват някои проблеми с този подход "клас-като-обект". Тъй като методите на класа са статично обвързани, вашият клас като обект няма да се ползва от предимствата на гъвкавостта на полиморфизма и актуализирането. (За дефиниции на полиморфизъм и динамично свързване вижте статията за Техники за проектиране, Състав срещу наследяване.) Полиморфизмът е възможен и актуализиране полезно чрез динамично свързване, но методите на класа не са динамично обвързани. Ако някой подкласира вашия клас-като-обект, той няма да може да замени методите на вашия клас, като декларира методите на класа със същото име; те ще могат само да се скрияттях. Когато се извика един от тези предефинирани методи на клас, JVM ще избере изпълнението на метода, което да се изпълнява не от класа на обект по време на изпълнение, а от типа на променлива по време на компилация.

В допълнение, безопасността на нишката и целостта на данните, постигнати чрез щателно прилагане на методите на класа във вашия клас като обект, е като къща, построена от слама. Безопасността на вашата нишка и целостта на данните ще бъдат гарантирани, стига всички да използват методите на класа, за да манипулират състоянието, съхранено в променливите на класа. Но невнимателен или невеж програмист би могъл, с добавянето на един екземпляр метод, който директно осъществява достъп до вашите променливи от частния клас, неволно да се хвърли и да издуха и да издуха безопасността на вашата нишка и целостта на данните.

Поради тази причина основната ми насока относно променливите на класа и методите на класа е:

Не третирайте класовете като обекти.

С други думи, не проектирайте със статични полета и методи на клас, сякаш са полетата на екземпляра и методите на обект.

Ако искате някакво състояние и поведение, чийто живот съвпада с този на клас, избягвайте да използвате променливи на класа и методи на клас, за да симулирате обект. Вместо това създайте действителен обект и използвайте променлива на клас, за да задържите препратка към него и методи на клас, за да осигурите достъп до препратката към обекта. Ако искате да се уверите, че само едно копие на някакво състояние и поведение съществува в едно пространство от имена, не се опитвайте да проектирате клас, който симулира обект. Вместо това създайте сингълтон - обект, за който се гарантира, че има само един екземпляр на пространство от имена.

И така, за какво са добри членовете на класа?

Според мен най-добрият начин на мислене, който да се култивира при проектирането на Java програми, е да се мислят обекти, обекти, обекти. Съсредоточете се върху проектирането на страхотни обекти и мислете за класовете предимно като чертежи за обекти - структурата, в която дефинирате променливите на екземпляра и методите на екземпляра, които съставят вашите добре проектирани обекти. Освен това можете да мислите за класове като за предоставяне на няколко специални услуги, които обектите не могат да предоставят или не могат да предоставят толкова елегантно. Мислете за класовете като:

  • подходящото място за дефиниране на "полезни методи" (методи, които вземат вход и предоставят изход само чрез предадени параметри и връщаната стойност)
  • начин за контрол на достъпа до обекти и данни

Полезни методи

Методи, които не манипулират или използват състоянието на обект или клас, наричам „полезни методи“. Помощните методи просто връщат някаква стойност (или стойности), изчислена единствено от данни, предадени на метода като параметри. Трябва да направите такива методи статични и да ги поставите в класа, най-тясно свързан с услугата, която методът предоставя.

Пример за полезен метод е String copyValueOf(char[] data)методът на класа String. Този метод генерира своята изходна стойност, върната стойност от тип String, единствено от входния параметър, масив от chars. Тъй като copyValueOf()нито използва, нито влияе върху състоянието на който и да е обект или клас, това е полезен метод. И, както всички полезни методи трябва да бъдат, copyValueOf()е метод на клас.

Така че един от основните начини за използване на методите на класа е като полезни методи - методи, които връщат изхода, изчислен само от входните параметри. Други приложения на методите на класа включват променливи на класа.

Променливи на класа за скриване на данни

Един от основните правила в обектно-ориентираното програмиране е скриването на данни - ограничаване на достъпа до данни, за да се намалят зависимостите между частите на програмата. Ако конкретна част от данните има ограничена достъпност, тези данни могат да се променят, без да се нарушават тези части от програмата, които не могат да получат достъп до данните.

Ако например обект е необходим само от екземпляри на определен клас, препратка към него може да се съхранява в променлива на частен клас. Това дава на всички екземпляри от този клас удобен достъп до този обект - екземплярите просто го използват директно - но никой друг код никъде другаде в програмата не може да го получи. По подобен начин можете да използвате променливи за достъп до пакети и защитени променливи, за да намалите видимостта на обектите, които трябва да се споделят от всички членове на пакет и подкласове.

Променливите на публичния клас са различна история. Ако променливата на публичен клас не е окончателна, тя е глобална променлива: онази гадна конструкция, която е антипод на скриването на данни. Никога няма извинение за променлива на публичен клас, освен ако тя не е окончателна.

Окончателните променливи на публичния клас, независимо дали са примитивен тип или препратка към обект, служат на полезна цел. Променливите от примитивни типове или от тип Stringса просто константи, които като цяло помагат да се направят програмите по-гъвкави (по-лесни за промяна). Кодът, който използва константи, е по-лесен за промяна, защото можете да промените постоянната стойност на едно място. Публичните променливи на окончателния клас на референтните типове ви позволяват да дадете глобален достъп до обекти, които са необходими в световен мащаб. Например System.in, System.outи System.errса публични крайните променливи клас, които дават достъп до глобалната стандартните входно изходни и грешки потоци.

По този начин основният начин за преглед на променливите на класа е като механизъм за ограничаване на достъпността на (което означава, да се скрие) променливи или обекти. Когато комбинирате методи на клас с променливи на класа, можете да внедрите още по-сложни политики за достъп.

Използване на методи на клас с променливи на класа

Освен че действат като полезни методи, методите на класове могат да се използват за контрол на достъпа до обекти, съхранявани в променливи на класа - по-специално, за да се контролира как обектите се създават или управляват. Два примера за този вид метод клас са setSecurityManager()и getSecurityManager()методите на класа System. Мениджърът на защитата за приложение е обект, който, както стандартните потоци за въвеждане, извеждане и грешки, е необходим на много различни места. За разлика от стандартните обекти на I / O поток обаче, препратка към мениджъра на защитата не се съхранява в публична променлива на крайния клас. Обектът на мениджъра на сигурността се съхранява в променлива на частен клас, а методите set и get реализират специална политика за достъп за обекта.

Моделът за сигурност на Java поставя специално ограничение за мениджъра на сигурността. Преди Java 2 (по-рано известен като JDK 1.2), приложението започна своя живот без мениджър на сигурността ( getSecurityManager()върнато null). Първото обаждане до setSecurityManager()установения мениджър по сигурността, който след това нямаше право да променя. Всички следващи обаждания до setSecurityManager()биха довели до изключение за сигурността. В Java 2 приложението винаги започва с мениджър на защитата, но подобно на предишните версии, setSecurityManager()методът ще ви позволи да промените мениджъра на защитата най-много веднъж.

Мениджърът на защитата предоставя добър пример за това как методите на класа могат да се използват заедно с променливи на частния клас за реализиране на специална политика за достъп за обекти, посочени от променливите на класа. Освен методите на полезността, помислете за методите на класовете като за средства за установяване на специални политики за достъп за препратки към обекти и данни, съхранявани в променливи на класа.

Насоки

Основният съвет, даден в тази статия, е:

Не третирайте класовете като обекти.

Ако имате нужда от обект, направете обект. Ограничете използването на променливи и методи на клас до дефиниране на полезни методи и прилагане на специални видове политики за достъп за обекти и примитивни типове, съхранявани в променливи на класа. Въпреки че не е чист обектно-ориентиран език, Java все пак е обектно-ориентиран до голяма степен и вашите проекти трябва да отразяват това. Мислете предмети.

Следващият месец

Статията за техниките за дизайн на следващия месец ще бъде последната от тази колона. Скоро ще започна да пиша книга, базирана на материала „Техники за проектиране“, „ Гъвкава Java“ , и ще поставя този материал на моя уебсайт, докато отивам. Така че, моля, следвайте проекта заедно и ми изпратете обратна връзка. След почивка от месец или два, ще се върна в JavaWorld и SunWorld с нова колона, фокусирана върху Jini.

Искане за участие на читатели

Насърчавам вашите коментари, критики, предложения, пламъци - всякакви отзиви - относно материала, представен в тази колона. Ако не сте съгласни с нещо или имате нещо да добавите, моля да ме уведомите.

Можете да участвате в дискусионен форум, посветен на този материал, да въведете коментар чрез формуляра в долната част на статията или да ми изпратите имейл директно чрез връзката, предоставена в биографията ми по-долу.

Бил Венърс пише софтуер професионално от 12 години. Базиран в Силициевата долина, той предоставя софтуерни консултации и услуги за обучение под името Artima Software Company. През годините той е разработил софтуер за потребителската електроника, образованието, полупроводниците и животозастраховането. Програмирал е на много езици на много платформи: асемблерен език на различни микропроцесори, C на Unix, C ++ на Windows, Java в мрежата. Автор е на книгата Inside the Java Virtual Machine, издадена от McGraw-Hill.