|
~BöLÜM-4~ |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ErişimErişim konusu kütüphaneler için çok büyük bir önem taşımaktadır.Erişim konusunda iki taraf vardır , bir tanesi kütüphaneyi kullanan kişiler (client) , diğeri ise bu kütüphaneyi yazan kişilerdir. Olaylara kütüphaneleri kullanan kişilerin bakış açısından bakarsak , istenen durum , bir kütüphanenin ilk versiyonunu kullanan bir uygulamanın , bu kütüphanenin ikinci , üçüncü ,dördüncü.... n. inci versiyonlarında bile uygulamada herhangi bir uyumsuzluk sorununun yaşanmamasıdır. Kütüphanenin her yeni versiyonununu , -ki yeni versiyon demek daha az hatalı , daha gelişmiş bir kütüphane anlamına gelir , sistemimize entegre ettiğimizde , bu kütüphaneyi kullanan her uygulamayı değiştirmeye kalkışsak hayat çekilmez olurdu herhalde . Olaylara birde kütüphaneleri yazan kişilerin bakış açısı ile bakalım . Bir kütüphane yazdınız , ve bunu kullanılması için internete koydunuz , bu kütüphane çok tutuldu , herkes tarafından tebrik mesajları geliyor herşey çok güzel .Fakat daha sonradan bu kütüphane içersinde bir hata olduğunu fark ettiniz veya bu kütüphane içersindeki bazı kısımları , daha hızlı çalışması için optimize etmek istiyorsunuz . Kütüphaneye yazan kişinin bu arzuları , bu kütüphane üzerinde nasıl bir etki oluşturur . Kütüphaneyi kullanan kişiler bundan zarar görebilir mi ? veya zarar görmemesi için ne yapılması gerekir. Bu tür problemlerin çözümü için devreye erişim kavramı girir , Java toplam dört adet erişim belirliyicisi sunar . En erişilebilirden en erişilmeze doğru sıralarsak , public , protected , friendly , private dır. Bu erişim belirliyicileri sayesinde hem kütüphanleri yazan kişiler özgürlüklerini kavuşur , hemde kütüphaneyi kullanan kişiler kullandıkları kütüphanenin yeni bir versiyonunu rahatlıkla sistemlerini entegre edebilirler. Kütüphaneleri tasarlayan kişiler , ileride değişebilecek olan sınıfları , veya sınıflara ait metodları , kullanıcı tarafından erişilmez yaparak , hem kütüphanenin rahatça gelişmesini sağlamış olurlar hemde kütüphaneyi kullanan kişiler endişelerini gidermiş olurlar."Herşey tamam ama paket ne anlama gelmektedir" , diyenler için gerekli açıklamaları yapalım. Paket (package)Paketler kütüphaneyi oluşturan elemanlardır . Paket mantığının var olmasında ana sebeblerden biri , isim karmaşasını çözmektir. Örneğin elimizde iki sınıf bulunsun X sınıfı ve Y sınıfı. Bu sınıflar içersinde aynı isimde iki metod olması , örneğin f() metodu , herhangi bir karmaşıklığa sebebiyet vermiyecektir.Sebebi ise bu aynı isimdeki metodların ayrı sınıflarda bulunmasıdır.Peki ya sınıf isimleri ? Sistemimizde bulunan aynı isimdeki sınıflar karmaşıklığa sebeb vermez eğer ki bu aynı isimdeki sınıflar değişik paketlerin içersinde bulunursa . gösterim-1
Yukarıdaki gösterimimizde BufferedReader sınıf isminin java.io paketinde tek olduğunu anlıyoruz (java.io paketi Java ile gelen standart bir pakettir.).Fakat başka paketlerin içersinde BufferedReader sınıf ismi rahatlıkla kullanılabilir. Paketin içersindeki tek bir sınıfı kullanmak yerine , paketin içersindeki tüm sınıfları tek seferde kullanmak için : gösterim-2
java.io paketinin içersinde sınıfları kendi uygulamalarımızda kullanmak için, sadece import java.io.* dememiz yeterli olacaktır. ör-paket-kullanim
Yukarıda örneğimizde görmediğimiz yeni kavram mevcuttur , "throws Exception" . İstisnalar(Exception) konusunu ilerleyen bölümlerde detaylı bir şekilde inceliyecegiz. Varsayılan Paket (Default Package)Öncelikle şunu tekrardan belirtelim , .java uzantılı fiziksel dosyayı derlediğimiz zaman , .java uzantılı fiziksel dosyanın tam ismine karşılık gelen .class fiziksel dosyası elde ederiz (tabii *.java dosyasında hata olmadığını farzederek) .Eğer .java fiziksel dosyasında birden fazla sınıf tanımlanmış ise , tanımlanan bu her sınıf için ayrı ayrı .class fiziksel dosyaları Java tarafından üretilir. ör-varsayılan-paket-1 - Test1.java
Yukardaki örneğimizi , Test1.java adıyla herhangi dizine kaydettim (fiziksel java uzantılı dosya ile public sınıfın ismi birebir aynı olmalıdır) , bu dosyayı javac komutu ile derlediğimde iki adet fiziksel .class dosyası elde ederim ; Test1.class ve Test2.class . Test1.java dosyanın en başına herhangi bir paket ibaresi yerleştirmediğimden dolayı , Java bu sınıfları varsayılan paket (default package) olarak algılıcaktır. ör-varsayılan-paket-2 - Test3.java
Aynı şekilde bu örneğimizide aynı dizine Test3.java adıyla kaydettip derlenikten sonra genel bakış aşağıdaki gibidir.
Şekil-1 Paket OluşturmaKendi paketlerimizi oluşturmanın temel amaçlarında biri , aynı amaca yönelik işleri yapan sınıfları bir çatı altında toplamaktadır.Bu sayede yazılan sınıflar daha derli toplu olur , artı aranılan sınıflar daha kolay bulunabilir. Peki eğer kendimiz paket oluşturmak istersek bunu nasıl başaracağız ? ör-paket-2
ör-paket-2 örneğimizde , Test1.java fiziksel dosyasını işletim sistemimizin herhangi bir dizine yerleştiremeyiz çünkü o artık tr.edu.kou.util paketine ait bir sınıfdır , bu sebepten dolayı içinde bulunması gereken dizinde bu paket ismiyle pararlel olmalıdır.
Şekil-2 Paket isimlerine bulunan çözüm , şu andaki internet alan isim sistemiyle(Internet Domain Name System) aynıdır.Örneğin Kocaeli Üniversitesinde matematik paketi yazan bir kişi , bu paketin içersindeki sınıfların,dünyanın herhangi bir yerindeki başka kütüphanenin paketleri içersindeki sınıfların ismiyle çakışmaması için internet alan isim sistemini kullanmalıdır. Internet alan isim sistemi , www.kou.edu.tr adresinin dünya üzerinde tek olduğunu garantiler. Aynı mantığı paket isimlerine uygulayarak , paketlerin içersindeki sınıf ismi problemine çözüm bulmuş oluruz . Dikkat edilecek olursa , paket isimleri , Internet alan isimlerinin tersden yazılmış halleridir , işletim sistemlerinin farkı göz önüne alınarak , math paketine ait sınıfların içinde bulunması gereken dizin , Unix veya Linux için tr/edu/kou/math , Window için tr\edu\kou\math şekilde olmalıdır.
Şekil-3 CLASSPATH ayarlarıJava yorumlayıcısı (interpreter) ; işletim sisteminin çevre değişkenlerinden CLASSPATH değişkenini bakarak import ifadesindeki paket'i bulmaya calisir. gösterim-3
Diyelim ki math paketi aşağıdaki dizinde bulunsun : gösterim-4
Java yorumlayıcısının , import ifadesindeki paket'i bulması için aşağıdaki tanımı CLASSATH'e eklememiz gerekmektedir. gösterim-5
CLASSPATH'e eklenen bu tanım sayesinde , Java yorumlayıcısı , C:\kodlar\bolum4\ dizinine kadar gidip , tr\edu\kou\math dizinini arıyacaktır , eğer -ki böyle bir dizin bulunursa , bu dizinin altındaki tüm sınıflar uygulama tarafından kullanılabilecek hale gelecektir.
Şekil-4
DortIslem.java , dosyasını C:\kodlar\bolum4\tr\edu\kou\math dizinine yerleştirelim : ör-dört-işlem
Gerekli CLASSPATH , tanımlarını yaptıktan sonra , bu paketi kullanacak olan uygulamamızı , işletim sistemimizin herhangi bir dizinine yerleştirebiliriz.
Uygulamanın çıktısı aşağıdaki gibidir:
Önemli noktaDikkat ettiyseniz , CLASSPATH değişkenini tanımlarken , tanımın en sonuna "." nokta koyduk.
Şekil-5 Bu önemli bir ayrıntıdır. Bu noktanın konmasındaki neden , varsayalın paketlerin(default package) içindeki sınıfların birbirlerini görebilmesini sağlamaktır.Bu nokta unutulursa , anlamsız hata mesajları ile karşılaşılabilir. Java 2 'yi sisteminize ilk kurduğunuz zaman basit örnekleri CLASSPATH değişkenine herhangi bir tanım eklemeden bile çalıştırabildiğinizi görürsünüz. Bunun sebebi Java 2 'nin temel kütüphanleri , kendi kurulum dizinine göre tahmin edebilmesinden ileri gelir. ÇakışmaAyrı paketin içersindeki , aynı isimdeki sınıfları uygulamızda kullanırsak ne olur ? Tahminen , isimleri aynı olsa bile değişik paketlerde bulundukları için bir sorun yaşanmaması gerekir. Öncelikle tr.edu.kou.util paketinin içersine kendi ArrayList sınıfımızı yazalım . ör-io-arraylist
Aşağıdaki örneği işletim sisteminizin herhangi bir dizinine kayıt edebilirsiniz. ör-çakışma-1
Cakisma.java dosyasını javac komutu ile derlediğimiz zaman , aşağıdaki hata mesajı ile karşılaşırız.
Bu hata mesajının anlamı , ArrayList sınıfının hem java.util paketinde hemde tr.edu.kou.util paketinde bulunmasından kaynaklanan bir ikilemi ifade eder. Uygulamda ArrayList sınıfı kullanılmıştır ama hangi paketin içindeki ArrayList sınıfı ? Bu sorundan kurtulmak için aşağıdaki örneğimizi inceliyelim ör-çakışma-2
Eğer ortada bir ikilem varsa , hangi sınıfını kullanmak istiyorsanız ,o sınıfın içinde bulunduğu paket isimini de yazarak , oluşan ikilemi ortadan kaldırabilirsiniz. Paket içersindeki uygulamaları çalıştırmakPaketlerin içersindeki tek başına çalışabilen uygulamalarımızı (standalone), herhangi bir yerden önüne sadece paket açıklamasını yazarak çalıştırmamız mümkündür. ör-dört-işlem-kullanım uygulamasının yeni versiyonu C:\kodlar\bolum4\tr\edu\kou\math dizinine kaydedelim. ör-paket-uygulama
Artık Hesaplama sınıfımız tr.edu.kou.math paketinin yeni üyesidir. Paket içersindeki tüm java uzantılı dosyaları C:\kodlar\bolum4\tr\edu\kou\math javac *.java diyerek derlenikten sonra Hesaplama.class dosyasını java komutu kullanara çalıştırmayı deneyelim . gösterim-6
Bir aksilik var gibi , ekranda beliren hata mesajı aşağıdaki gibidir .
Hesaplama sınıfı bulanamıyor diye bir hata mesajı ? nasıl olur ama orda ?? evet orada ama o artık Hesaplama sınıfı değildir , tr.edu.kou.math.Hesaplama sınıfıdır yani artık bir paketin üyesi olmuştur. Aşağıdaki ifade sayesinde işletim sisteminizin herhangi bir dizininden (- tabii CLASSPATH değişkeninin değeri doğru tanımlanmış ise ) tr.edu.kou.math.Hesaplama sınıfına ulaşıp onu java komutu ile çalıştırabiliriz. gösterim-7
Uygulamanın çıktısı aşağıdaki gibidir .
JAR Dosyaları (The JavaTM Archive File)JAR dosya formatı , birçok dosyayı arşivlemenize ve sıkıştırmamıza olanak tanır.Normalde JAR dosylarının içersinde sınıf dosyaları bulunur.Bazı durumlarda özellikle appletler de yardımcı dosyalarda(gif , jpeg ...) JAR dosyasının içersinde konulabilir. JAR dosyasının sağladığı faydalar aşağıdaki gibidir.
Oluşturduğumuz paketlerimizi JAR dosyasının içersine yerleştirerek daha derli toplu bir görüntü elde etmiş oluruz. tr.edu.kou.math ve tr.edu.kou.util paketlerini tek bir JAR dosyasında birleştirmek için aşağıdaki komutu kullanmamız yeterli olur ama JAR dosyasını oluşturmak için yazılan komutun hangi dizinde çağrıldığı önemlidir. JAR-dosyası işlemleri için gerekli olan bazı komutlar :
tablo-1 gösterim-8
Gösterim-8'deki ifadeyi C:\kodlar\bolum4 dizinin içersinde iken çalıştırmalıyız ki , JAR dosyasının içersine doğru yapıdaki dizinleri yerleştirelim.
Şekil-6
Oluşmuş olan bu JAR dosyasını CLASSPATH ekleyerek , Javanın bu paketleri bulmasını sağlıyabilirsiniz.Aşağıdaki ifade yerine : gösterim-9
bu ifadeyi kullanabilirsiniz . gösterim-10
Java CLASSPATH değerindekiden yola çıkarak JAR dosyasını bulup açar , tr\edu\kou\util ve tr\edu\kou\math dizinlerine erişebileceğinden bir sorun yaşanmıyacaktır.Yani JAR dosyasının hangi dizinde olduğu önemli değildir , önemli olan CLASSPATH'de olmasıdır.Tabii paketlerin içersindeki sınıflar geliştikçe güncelliği korumak adına JAR dosyasını tekrardan üretmek gerekecektir. Erişim BelirliyicilerJava programlama dilinde , 4 tür erişim belirliyicisi vardır ; friendly public , protected ve private . Bu erişim belirliyicileri global olan değişkenlerde(statik veya değil) ve metodlarda (statik veya değil) kullabilirsiniz .Ayrıca sınıflarımıza erişimlerde de sadece public ve friendly erişim belirliyicilerini kullanırız. Friendly (Arkadaşca)Friendly erişim belirliyicisi , global değişkenlere (statik veya değil) , metodlara (statik veya değil) ve sınıflara atanabilir.Friendly türünde erişim belirliyicisine sahip olan global değişkenler (static veya değil) , içinde bulundukları paketin diğer sınıfları arafından erişilebilirler.Fakat diğer paketlerin içersindeki sınıflar tarafından erişilemezler. Yani diğer paketlerin içerisindeki sınıflara karşı private erişim belirliyici etkisi oluşturmuş olurlar.Friendly metodlarda (statik veya değil) yanlız aynı paketin içersindeki sınıflar tarafından erişilebilirler , başka paketlerin içersindeki sınıflar tarafından erişilemezler. Aynı şekilde sınıflarada friendly erişim belirliyicisi atayabiliriz, böylece aynı paketeki diğer sınıflar tarafından erişilebilir ama diğer paketlerin içersindeki sınıflar tarafından erişilemezler. Şimdi tr\edu\kou\ dizinin altina yeni bir dizin oluşturalim , ve ismini gerekli verelim, yani tr\edu\kou\gerekli paketini oluşturmuş oldum. Bu paketin içersine iki adet frindly sınıf yazalım , Robot ve Profesor sınıfları
ör-friendly-Robot
ör-friendly-Profesor
Yukarıdaki örneklerimizden anlaşılacağı üzere bir global değişkeni (statik veya değil) veya bir sınıfı friendly yapmak istiyorsak önüne hiç bir erişim belirliyicisi koymayız. Şimdi bu iki sınıf aynı paketin içinde olduklarına göre Profesor sınıfı rahatlıkla Robot sınıfına erişebilecektir.Peki başka bir paketdeki sınıf Robot sınıfına erişebilir mi ? ör-friendly-Asistan
Daha evvelden belirtildiği gibi , friendly olan global değişkenlere (statik veya değil) veya sınıflara sadece içersinde bulunduğu paketin diğer sınıfları tarafından erişilir ,diğer paketlerin içersindeki sınıflar tarafından erişilemezler. Şekil üzerinde göstermeye çalışırsak :
Şekil-7 Varsayılan Paketlerde (Default Package ) ErişimAşağıdaki iki java dosyasını işletim sistemimin herhangi bir dizinine kaydetttim. ör-alt-komşu
ör-üst-komşu
Bu iki sınıf friendly erişim belirliyisine sahiptir yani aynı paketin içersindeki sınıflar tarafından erişilebilirler ama ortada herhangi bir paket ibaresi bulunmamaktadır. Aynı dizinde olan fakat bir paket olarak tanımlanmamış sınıfların , Java tarafından varsayılan paket çatısı altında toplandığını biliyoruz. Bu iki sınıfın birbirini görebilmesi için CLASSPATH değişkenin değerinde "." (nokta) ibaresinin olması şarttır (bkz). Public (Herkese açık)Public erişim belirliyicisi kullanıdığınız global değişkenler (statik veya değil) veya metodlar (statik veya değil), herkes tarafından erişilebilir hale gelir.Bu erişim belirliyicisini yerleştirmeden evvel iki kere düşünmenizi tavsiye ederim. Bu erişim belirliyicisine sahip olan global değişkenler (statik veya değil) veya metodlar (statik veya değil ) herhangi bir yerden direk olarak çağrılabildiklerinden dolayı , dış dünya ile paketiniz arasındaki arabirim rolünü üstlenirler. ör-public-makine
tr.edu.kou.util paketinin içersinde Makine sınıfının iki adet global obje değişkeni bulunmaktadır , bunlardan int ilkel tipindeki devir_sayisi değişkeni friendly erişim belirliyicisine sahiptir , yani sadece tr.edu.kou.util paketinin içersindeki diğer sınıflar tarafından direk erişilebilir. Diğer String tipindeki model değişkenimiz ise her yerden erişilebilir. degerAl() metodu public erişim belirliyicisine sahiptir yani heryerden erişilebilir.Aynı şekilde degerAta(int deger) metoduda her yerden erişilebilir ama calis() metodu friendly erişim belirliyicisine sahip olduğundan sadece aynı paketin içersindeki sınıflar tarafından erişilebilir. ör-makine-kullanım
Private (Özel)Private olan
global değişkenlerimiz (statik veya değil) veya methodlarımıza (sınıflar
private olamazlar), aynı paket içinden olsun , veya farklı paketlerden
olsun erişilemezler .Ancak ait
olduğu sınıfın içinden erişilebilirler
. Bu sayede eğer bir kütüphane yazarsanız , o kütüphaneyi kullanan
kullanıcıların kodlarına zarar vermeden , yeni üst versiyonlar hazırlayabilirsiniz
. Private erişim belirliyici sizin için özgürlük anlamina
gelebilir. ör-private-1
ör-private-2
Kahve sınıfının yapılandırıcısı (constructor) private olarak tanımlanmıştır . Bu sebebten herhangi bir başka sınıf , Kahve sınıfının yapılandırıcısı direk olarak çağıramaz ,aynı paketin içersinde olsa dahi . Ama bu private yapılandırıcı aynı sınıfın içersindeki metodlar tarafından rahatlıkla çağırılabilir. Aynı şekilde private olarak tanımlanmış global değişkenelere (statik veya değil) veya metodlara , aynı paket içersinde olsun veya olmasın , kesinlikle erişilemez. Şekil üzerinde gösterirsek : Şekil-8 Bu örnekte Kahve Objesini direk olarak oluşturamıyoruz yani kendimiz direk olarak kafe'nin mutfağından kahve alamiyoruz , kahve içmek için kesinlikle garson a siparis vermeliyiz . Bizde siparisGarson() metodu ile siparişlerimizi garson'a iletmiş oluyoruz. Protected (Şartlı erişim)Sadece global değişkenler (statik veya değil) ve metodlar(statik veya değil) protected erişim belirliyicise sahip olabilirler .Sınıfların protected erişim belirliyicisine sahip olma imkanı yoktur , sınıflar friendly veya public erişim belirliyicisine sahip olabilirler. Protected erişim belirliyicisi kalıtım(inheritance) konusu ile sıkı sıkıya bağlıdır.Kalıtım konusunu bir sonraki bölümde ele alınıcaktır. Kalıtım konusundan kısaca bahsedersek , ana bir sınıfdan diğer sınıfların türemesi diyebiliriz. gösterim-11
Gösterim-7 deki ifade şunu der : Her Kedi bir Hayvandır.Yani Hayvan sınıfından Kedi ürettik,bizim oluşturacağımız her Kedi objesi bir Hayvan olacaktır ama kendine has kedisel özelliklerde taşıcaktır. Protected erişim belirliyicisini evdeki buzdolabımızın kapısına vurulmuş bir kilit olarakta düşünebiliriz.Örneğin evimizde bir buzdolabı var ve içinde her türlü yiyecek ve içecek mevcut ,biz aile büyüğü olarak bu buzdolabına herkesin erişmesini istemiyoruz (public yaparsak) aksi takdirde yiyecek ve içecek kısa sürede bitip ailenin aç kalma tehlikesi oluşacaktır , aynı şekilde bu buzdolabına erişimi tamamen kesersek de aile bireyleri aç kalacaktır (private yaparsak). Tek çare özel bir erişim belirliyicisi kullanmaktır yani sadece aileden olanlara ve komşulara(aynı paketin içersindeki sınıflara) bu buzdolabının erişmesine izin veren bir erişim belirliyicisi yani protected erişim belirliyicisi. ör-hayvan-1
Hayvan sınıfından türetilen Kedi sınıfını tr.edu.kou.gerekli paketinin içersinde yazarsak ör-hayvan-2
Kedi.java dosyasını önce derleyip(compile) gösterim-12
Sonrada çalıştıralım. gösterim-13
Uygulamanın çıktısı aşağıdaki gibi olur:
Şekil-9 Encapsulation (Kapsüllenme)Nesneye yönelik programlamanın (Object oriented programming) en özelliklerinden biri kapsüllenmedir. Kapsüllenme , dışarıdaki başka bir uygulamanın , bizim objemize olan erişimlerini kontrol altında tutma mantığıdır. Bu kavrama göre objemizin içersinde dışaradan başka bir uygulamanın erişibilmesi için arabirimler olması gerekir , ama objemizin hesaplamaları veya problemlere yönelik algoritmaların çalıştırdığı içsel , ileride değişebilecek gizli bir bölümün olması gerektiğidiği belirtir.Böylece ileride değişme riski olan algoritma veya hesaplamalar kullanıcıya yansıtılmaz. Kapsüllenme , dışarıdaki başka uygulamanın , bizim objemiz ile sadece arabirimler (public) sayesinde iletişim kurması gerektiği , ama arka planda işi yapan esas kısmın gizlenmesini söyler (friendly , protected veya friendly).Olaylara bu açıdan bakarsak objemizi iki kısma bölmeliyiz , arabirimler , objenin dünya ile iletişim kurabilmesi için ve gemiyi yürüten kısım , yani hesaplamaların veya problemlere özgü algoritmaların yer aldığı gizli bölüm. ör-kapsüllenme-1
Yukarıdaki örneğimizde , bu Makine2 objemize sadece get() ve set() metodları ile ulaşabiliriz , geriye kalan global obje değişkenlerimize veya calis() metoduna aynı paketden veya başka herhangi bir paket içersinden ulaşım söz konusu değildir.Kapsüllenme kavramının dediği gibi , objemiz iki kısımdan oluşturduk , ara birimler ( get() , set() ) ve gemiyi yürüten kısım ( calis() ) . Başka paketin içersinde olan başka bir uygulamanın , tr.edu.kou.util.Makine2 objesinin sadece iki metoduna erişebilir, get() ve set(). ör-kapsüllenme-2
Genel bir bakışSınıflar için erişim matrisleri aşağıdaki gibidir :
Sınıflar protected veya private olamazlar , bu bağlamda matrisimizi şöyle okuyabiliriz ; örneğin elimizde A sınıfı bulunsun
Statik veya statik olmayan metodlar için , erişim matrisi aşağıdaki gibidir.
Metodlar (statik veya değil) public , protected , friendly ,private olabilirler.Örneğin public X sınıfının içersinde f() metodumuz olsun .
Statik veya statik olmayan global değişkenler için , erişim matrisi aşağıdaki gibidir.
Global değişkenler (statik veya değil) public , protected , friendly ,private olabilirler.Örneğin public X sınıfının içersinde "String uzunluk" adında değişkenimiz olsun .
Sorular
Bu dökümanın her hakkı saklıdır. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||