Kısacası, yazılımınızı yeniden kullanılabilirlik için tasarlamayın, çünkü işlevlerinizin tekrar kullanılıp kullanılmayacağını son kullanıcı umursamaz. Bunun yerine, tasarım anlaşılabilirliği için mühendis - kodum başkası için kolay mı yoksa geleceğim için unutkan bir benlik mi? - ve tasarım esnekliği- kaçınılmaz olarak hataları düzeltmem, özellikler eklemem veya işlevleri değiştirmem gerektiğinde kodum değişikliklere ne kadar direnç gösterecek? Müşterinizin umursadığı tek şey bir hatayı bildirdiğinde veya bir değişiklik istediğinde ne kadar çabuk cevap verebileceğinizdir. Tasarımınızla ilgili bu soruları sormak, tekrar kullanılabilecek bir kodla sonuçlanma eğilimindedir, ancak bu yaklaşım, söz konusu kodun kullanım ömrü boyunca karşılaşacağınız gerçek sorunlardan kaçınmaya odaklanmanızı sağlar; "mühendislik" boyun sakallarını memnun etmek için idealdir.
Sağladığınız örnek kadar basit bir şey için, ilk uygulamanızın ne kadar küçük olduğundan dolayı gayet iyi, ancak bu kadar basit bir tasarımın (esnekliğin aksine) çok fazla işlevsel esneklik sıkıştırarak denemeyi zorlaştırması bir prosedür. Aşağıda, kendileriyle ne demek istediğimi göstereceğimi umduğum gibi anlaşılabilirlik ve esneklik için karmaşık sistemler tasarlamaya tercih ettiğim yaklaşımım açıklanmaktadır. Bu stratejiyi tek bir prosedürde 20 satırdan daha az satırda yazılabilecek bir şey için kullanmayacağım, çünkü çok küçük bir şey zaten olduğu gibi anlaşılabilirlik ve esneklik kriterlerime uyuyor.
İşlemler değil Nesneler
Eski okul modülleri gibi sınıfları, yazılımınızın yapması gereken şeyleri yerine getirmek için çağırdığınız bir grup yordamı kullanmak yerine, etki alanını elinizdeki görevi gerçekleştirmek için etkileşime giren ve işbirliği yapan nesneler olarak modellemeyi düşünün. Nesneye Yönelik bir paradigmadaki yöntemler, aslında, nesneler arasındaki sinyaller olarak yaratıldı; böylece , ne olduğunu ve muhtemelen bir geri dönüş sinyali Object1
alabileceğini söyleyebildi Object2
. Bunun nedeni, Nesne Yönelimli paradigmanın, emeratif paradigmanın aynı eski işlevlerini ve prosedürlerini düzenlemek için süslü bir yol yerine etki alanlarınızı ve etkileşimlerini modellemektir. Durumundavoid destroyBaghdad
Örneğin, Bağdat'ın ya da başka bir şeyin (hızlı, karmaşık, anlaşılması zor ve kırılgan bir şekilde büyüyebilecek olan) imhasını ele almak için bağlamsız bir jenerik yöntem yazmaya çalışmak yerine, yok edilebilecek her şeyin nasıl olduğunu anlamaktan sorumlu olması gerekir. kendini yok etmek. Örneğin, yok edilebilecek şeylerin davranışını tanımlayan bir arayüze sahipsiniz:
interface Destroyable {
void destroy();
}
O zaman bu arayüzü uygulayan bir şehir var:
class City implements Destroyable {
@Override
public void destroy() {
...code that destroys the city
}
}
Bir örneğin imhasını gerektiren hiçbir şey bunun City
nasıl olacağını asla umursamaz, bu nedenle, bu kodun dışında bir yerde var olmanın hiçbir nedeni yoktur City::destroy
ve gerçekten de, City
kendi dışının iç işleyişine ilişkin samimi bilgiler, azalan sıkı bir bağlantı olacaktır. felksilite, o dış unsurları göz önünde bulundurmanız gerektiğinden, davranışını değiştirmek zorunda kalmanız gerekir City
. Kapsüllemenin arkasındaki asıl amaç budur. Her nesnenin kendi API'sine sahip olduğunu ve onunla ihtiyaç duyduğunuz her şeyi yapmanıza izin vermesi gerektiğini düşünerek isteklerinizi yerine getirme konusunda endişelenmesine izin verin.
Delegasyon, "Kontrol" değil
Şimdi, uygulayıcı sınıfınızın olup olmadığı City
veya Baghdad
şehri tahrip etme sürecinin ne kadar jenerik olduğuna bağlı. Her ihtimalde, City
kentin toplam yıkımını gerçekleştirmek için ayrı ayrı yok edilmesi gereken daha küçük parçalardan oluşacak bir irade olacak, bu durumda, bu parçaların her biri de uygulanacak Destroyable
ve her biri tarafından City
imha edilmesi talimatı verilecek . kendileri de aynı şekilde dışarıdan biri City
kendini yok etmesini istedi .
interface Part extends Destroyable {
...part-specific methods
}
class Building implements Part {
...part-specific methods
@Override
public void destroy() {
...code to destroy a building
}
}
class Street implements Part {
...part-specific methods
@Override
public void destroy() {
...code to destroy a building
}
}
class City implements Destroyable {
public List<Part> parts() {...}
@Override
public void destroy() {
parts().forEach(Destroyable::destroy);
}
}
Gerçekten delirmek ve Bomb
bir yere düşmüş olan fikrini uygulamak ve belirli bir yarıçaptaki her şeyi yok etmek istiyorsanız, şuna benzer bir şey olabilir:
class Bomb {
private final Integer radius;
public Bomb(final Integer radius) {
this.radius = radius;
}
public void drop(final Grid grid, final Coordinate target) {
new ObjectsByRadius(
grid,
target,
this.radius
).forEach(Destroyable::destroy);
}
}
ObjectsByRadius
Bomb
Girişlerden hesaplanan bir nesne kümesini temsil eder, çünkü Bomb
bu hesaplamanın nesnelerle çalışabildiği sürece nasıl yapıldığını önemsemez. Bu tesadüfen tekrar kullanılabilir, ancak asıl amaç hesaplamayı bırakma işlemlerinden izole etmektir.Bomb
nesneleri ve tahrip etme böylece her bir parçayı ve bunların nasıl bir araya geldiğini ve tüm algoritmayı yeniden şekillendirmek zorunda kalmadan tek bir parçanın davranışını nasıl değiştirebileceğinizi kavrayabilmek. .
Etkileşimler, Algoritmalar değil
Karmaşık bir algoritma için doğru sayıda parametreyi tahmin etmeye çalışmak yerine, işlemi her biri son derece dar rollere sahip bir dizi etkileşim nesnesi olarak modellemek daha mantıklıdır, çünkü size karmaşıklığınızı modelleme yeteneği verecektir. Bu iyi tanımlanmış, anlaşılması kolay ve neredeyse değişmeyen nesneler arasındaki etkileşimler yoluyla işlem yapar. Doğru yapıldığında, bu bile bir arayüz ya da iki uygulama ve sizin için hangi nesnelerin başlatıldığını yeniden işleme gibi önemsiz en karmaşık değişikliklerden bazıları yaparmain()
yönteminizde .
Size orjinal örneğinize bir şey verirdim, ama dürüst olmak gerekirse, "Gündüz Işık Tasarrufu" nu basmanın ne demek olduğunu çözemiyorum. Bu sorun kategorisi hakkında söyleyebileceğim, bir hesaplama yaptığınız zaman, bunun sonucu olarak birkaç şekilde biçimlendirilebilecek olan, yıkmak için tercih ettiğim yol şudur:
interface Result {
String print();
}
class Caclulation {
private final Parameter paramater1;
private final Parameter parameter2;
public Calculation(final Parameter parameter1, final Parameter parameter2) {
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
public Result calculate() {
...calculate the result
}
}
class FormattedResult {
private final Result result;
public FormattedResult(final Result result) {
this.result = result;
}
@Override
public String print() {
...interact with this.result to format it and return the formatted String
}
}
Örnekte Java kütüphanesinden bu tasarımı desteklemeyen sınıflar kullandığından, ZonedDateTime
doğrudan API'sini kullanabilirsiniz . Buradaki fikir, her bir hesaplamanın kendi nesnesi içinde kapsüllenmesidir. Kaç kez çalışması gerektiği veya sonucu nasıl biçimlendirmesi gerektiği konusunda hiçbir varsayımda bulunmaz. Sadece hesaplamanın en basit halini almakla ilgilidir. Bu, hem değişimini kolay hem de esnek hale getirir. Aynı şekilde, Result
sadece hesaplamanın sonucunu kapsamaya almakla ilgilenir ve FormattedResult
sadece Result
onu tanımladığımız kurallara göre biçimlendirmekle etkileşime girmekle ilgilenir . Böylece,Her biri iyi tanımlanmış bir göreve sahip olduklarından, her bir yöntemimiz için mükemmel argüman sayısı bulabiliriz . Arayüzler değişmediği sürece ilerlemenin değiştirilmesi de çok daha basittir (nesnelerinizin sorumluluklarını gerektiği gibi en aza indirmişseniz, bunun yapılması muhtemel değildir). Yöntemimizmain()
şöyle görünebilir:
class App {
public static void main(String[] args) {
final List<Set<Paramater>> parameters = ...instantiated from args
parameters.forEach(set -> {
System.out.println(
new FormattedResult(
new Calculation(
set.get(0),
set.get(1)
).calculate()
).print()
);
});
}
}
Nitekim, Nesne Yönelimli Programlama, özel olarak, Zorunlu paradigmanın karmaşıklığı / esneklik sorununa bir çözüm olarak icat edildi çünkü optimal olarak nasıl yapılacağına dair iyi bir cevap (herkesin hemfikir olabileceği veya bağımsız olarak gelebileceği). Deyim içindeki Emir Kipleri ve İşlevleri belirtiniz.