Konstruktory przy dziedziczeniu

0

Mam tak o:

abstract class Page {
    final PageContent content;
    final int id;

    public Page (PageContent content, int id) {
        this.content = content;
        this.id = id;
    }

    abstract Post getPost(int id);
}
class Page4programmers extends Page {
    public Page4programmers (PageContent content, int id) {
        super(content, id);
    }

    @Override
    Post getPost(int id) {
        PostFactory factory = new PostFactory4programmers();
        factory.createPosts(content).get(id - 1);
    }
}

działa, ale chciałbym dokonać dependency inversion i żeby PostFactory był zapodawany z zewnątrz i jednocześnie, żeby ten PostFactory był finalny. Musiałbym go dodać to do konstruktora, ale nie mogę tego dać przed `super(content, id), bo super musi być pierwsze

class Page4programmers {
     final PostFactory factory;
     public Page4programmers (PageContent content, int id, PostFactory factory) {
         this.factory = factory;         
         super(content, id);
     }

//...

}

a jak dam po to nic się nie stanie

class Page4programmers {
     final PostFactory factory;
     public Page4programmers (PageContent content, int id, PostFactory factory) {
         super(content, id);
         this.factory = factory;         
     }

//...

}

Załóżmy, że nie chcę dodawać tego do abstrakcyjnej klasy Page. Jak to zrobić najprościej?

1

Ale dtos czy tam obiekt domenowy nie powiem mieć referencji na factory oO. Co Ty w ogóle chcesz zrobić?

0
scibi92 napisał(a):

Ale dtos czy tam obiekt domenowy nie powiem mieć referencji na factory oO. Co Ty w ogóle chcesz zrobić?

czemu? Nie robię MVC.

chodzi o to bym se mógł napisać

Page page = new Page4programmers(content, 1);
for (Post : page) {
    // ...
}
0

Ale dlaczego chcesz mieć przypisanie this.factory = factory; przed wywołaniem super i co masz na myśli pisząc że "nic sie nie dzieje" a co ma się konkretnie dziać? Bo nie czaje.

0
MrMadMatt napisał(a):

Ale dlaczego chcesz mieć przypisanie this.factory = factory; przed wywołaniem super i co masz na myśli pisząc że "nic sie nie dzieje" a co ma się konkretnie dziać? Bo nie czaje.

No, chcę by tworząc obiekt Page4programmers wypełniał się jego atrybut factory, o którym Page nie wie.

0

Jeśli chcesz dostać się do pola, które konkretnie jest w Page4programmers, no to albo robisz castowanie, albo po prostu deklarujesz zmienną jako typ Page4programmers a nie Page.

0

Mnie zastanawia czemu chcesz tutaj w ogóle skorzystać z przekazywania fabryki i nie tworzyć jej wewnątrz klasy? Masz konkretne Page4programmers, więc jaki jest problem, że korzysta tylko i wyłącznie w środku z PostFactory4Programmers do tworzenia postów? A jeśli chcesz z zewnątrz przekazywać, to czemu jest problemem przypisanie po wywołaniu super, bo nie rozumiem co się nic nie stanie?

0
Michał Sikora napisał(a):

Mnie zastanawia czemu chcesz tutaj w ogóle skorzystać z przekazywania fabryki i nie tworzyć jej wewnątrz klasy? Masz konkretne Page4programmers, więc jaki jest problem, że korzysta tylko i wyłącznie w środku z PostFactory4Programmers do tworzenia postów? A jeśli chcesz z zewnątrz przekazywać, to czemu jest problemem przypisanie po wywołaniu super?

zrobiłem to fabryką, bo do tworzenia potrebne jest zainicjowanie 7 fabryk web scrapperów, wyciągnięcie wyniku z każdej takiej fabryki na różne sposoby, a na końcu jest wszystko składane builderem.

0

Ok, ale dalej nie czaję, co jest nie tak z tą kolejnością z super.

0

Nie mam pojęcia po co chcesz robić te roszady z factory ale może napisz sobie klasę wrapującą te dwie klasy gdzie będziesz miał:

class MyClassWrapper {
    Page page;
    Factory factory;
}

I stwórz powiązanie między Page i Factory jakąś trzecią klasą.

0

Nie rozumiem pytania. Czemu chcesz to dać przed super a nie możesz za? o_O

0
Julian_ napisał(a):

Mam tak o:

abstract class Page {
    final PageContent content;
    final int id;

    public Page (PageContent content, int id) {
        this.content = content;
        this.id = id;
    }

    abstract Post getPost(int id);
}
class Page4programmers extends Page {
    public Page4programmers (PageContent content, int id) {
        super(content, id);
    }

    @Override
    Post getPost(int id) {
        PostFactory factory = new PostFactory4programmers();
        factory.createPosts(content).get(id - 1);
    }
}

W pierwszym impulsie mi sie to wydało kodem Apache Wicket, dopiero w głębszym czytaniu pomyślałem, ze raczej nie.
Co do A.Wicket.

  • polecam. @Julian_ czym się kierowałeś dochodząc do tego punktu? Moze wejść w niego?
  • oficjalna dokumentacja usilnie odradza fabryki w takich kontekstach, ale to trudno wyrazić w krótki poście.
0

Dobra, macie cały kod, bo nie umiem tłumaczyć o co mi chodzi

public abstract class Page implements Iterable<Post> {
	public final int pageNumber;
	public final String url;
	protected final Document doc;

	
	public Page(Document doc, int pageNumber, String url) {
		this.pageNumber = pageNumber;
		this.url = url;
		this.doc = doc;
	}
	
	public abstract Post getPost(int idx);

	public abstract int getNumberOfPosts();

	public String getPageNumber() {
		String result = "";
		int div = 2 - (int) Math.log10(pageNumber); // all page number must have the same
											// number of digits for good sorting
											// as string.
		for (int i = 0; i < div; i++) {
			result = String.join("", "0", result);
		}
		result = result + Long.toString(pageNumber);
		return result;
	}
	
	@Override
	public Iterator<Post> iterator() {
		return this.new IteratorInner();
	}
	
	private class IteratorInner implements Iterator<Post> {
		private int count = 1;

		@Override
		public boolean hasNext() {
			return (count <= getNumberOfPosts());
		}

		@Override
		public Post next() {
			return getPost(count++); // posts counting
									// starts from 1
		}
	}

	@Override
	public String toString() {
		return doc.html();
	}
}
  
  
public final class Page4programmers extends Page {
	private Elements allPosts;

	public Page4programmers(Document doc, int pageNumber, String url) {
		super(doc, pageNumber, url);
	}
	
	// CAUTION!
	// THE posts are counted from 1.
	@Override
	public Post getPost(int idx) {
		if(allPosts == null) {
			initAllPosts();
		}
		try {
			return new Post4programmers(allPosts.get(idx - 1), idx);
		} catch (IndexOutOfBoundsException e) {
			throw new IllegalStateException("Number of post = " + idx + " is out of bounds.");
		}
	}

	@Override
	public int getNumberOfPosts() {
		if (allPosts == null) {
			initAllPosts();
		}
		return allPosts.size();
	}	
	
	private void initAllPosts() {
		// cuz the first post occurs always at every page and the last is a rubish.
		if (pageNumber == 1) {
			allPosts = doc.select("div.post[id!=last-post]");
		} else {
			allPosts = doc.select("div.post[id!=first-post][id!=last-post]");
		}
	}
}
  
  
public abstract class Post {
	public final long id;
	public final int postNumber;
	public final Message message;
	protected Element elem; // the core of the Post class.

	public Post(Element elem, int postNumber) {
		this.elem = elem;
		this.postNumber = postNumber;
		this.id = createId();
		this.message = createMessage();
	}
	
	protected abstract Message createMessage();
	
	protected abstract long createId();

	/**
	 * @return: when the post was post.
	 */
	public abstract Date getDate();

	public abstract String getAuthorNick();

	public abstract String getContent();

	public abstract String getContentText();
	
	public abstract String getContentWithoutQuotes();

	public abstract String getContentTextWithoutQuotes();

	public abstract Document toMongoDocument();
	
	@Override
	public String toString() {
		return this.getContentText();
	}
}
  
  
public final class Post4programmers extends Post {

	public Post4programmers(Element elem, int postNumber) {
		super(elem, postNumber);
	}
	
	@Override
	protected Message createMessage() {
		return new MessageFactory4programmers().createMessage(this.getContentWithoutQuotes().toLowerCase());
	}
	
	@Override
	protected long createId() {
		String tmp = elem.select("div[id^=post-]").attr("id");
		tmp = tmp.replaceAll("[^0-9]", ""); // becuz it's format is "post-546"
									        // where 546 is an example number
		return Long.parseLong(tmp);
	}

	@Override
	public Date getDate() {
		Element e = elem.select("span[class=\"timestamp\"]").first();
		long x = Long.parseLong(e.attr("data-timestamp"));
		return new Timestamp(x * 1000);
	}

	@Override
	public String getAuthorNick() {
		return elem.select("div[class=\"col-xs-12 col-user\"]").first().text();
	}
	
	@Override
	public String getContent() {
		return this.getCore().html();
	}
	
	@Override
	public String getContentText() {
		return this.getCore().text();
	}
	
	@Override
	public String getContentWithoutQuotes() {
		return this.getCoreWithoutQuotes().html();
	}
	
	@Override
	public String getContentTextWithoutQuotes() {
		return this.getCoreWithoutQuotes().text();
	}
	
	private Element getCore() {
		return elem.select("div.post-content").first();
	}
	
	private Element getCoreWithoutQuotes() {
		Element result = this.getCore();
		result.select("blockquote").remove(); // posts without blockquotes cuz this will double the information
		                                      // if wouldn't be removed.
		return result;
	}

	@Override
	public org.bson.Document toMongoDocument() {
		return new ConveterToMongoDocument().toMongoDocument(this);
	}
}

MessageFactory4programmers i ConveterToMongoDocument chcę zapodawać z zewnątrz

2

@Shalom: Ale zobacz, że w konstruktorze jest wywołane createMessage(), które korzysta z fabryki

Ok teraz rozumiem gdzie jest błąd. @julian błąd polega na tym że konstruktor nadklasy NIE MOŻE wołać metod które są nadpisywane w pod-klasie! Bo te metody odpalasz na potencjalnie jeszcze nie dokończonym obiekcie klasy pochodnej! To jest generalna zasada, zwyczajnie nie możesz tak robić.

1
Julian_ napisał(a):

Dobra, macie cały kod, bo nie umiem tłumaczyć o co mi chodzi

public abstract class Page implements Iterable<Post> {

Na to jest moje "NIE".
Fakt, owo "nie" by było mocniejsze gdyby było to dziedzinie klasowe, tu jest "zaledwie" implementowanie interfejsu, i ktoś moze się nie zgodzić. Ale ja tak myślę.

To nie spełnia relacji jest/is i wg mnie nie powinno dziedziczyć (nawet interfejsowo)
Za to bardzo dobrze spełnia relację ma/używa/has/uses czyli materiał na wzorzec "zawieranie zamiast dziedziczenia".

To w konsekwencji rzutuje na problem dyskutowany w watku, czyszcząc z problematycznego dziedziczenia, i sam problem zniknie/stanie się mniej napięty.

0

Mam pomysł jak to jeszcze bardziej zepsuć :] Wystarczy przenieść ciało konstruktora do osobnej metody inicjalizacyjnej i tym samym obejść standardową kolejność inicjalizacji:

class Greeter {
    void greet() {
        System.out.println("Hello, World!");
    }
}

abstract class SuperClass {
    SuperClass() {
        init();
    }

    void init() {
        doSomething();
    }

    abstract void doSomething();
}

class SubClass extends SuperClass {
    Greeter greeter;

    SubClass() { }

    @Override
    void init() {
        this.greeter = new Greeter();
        super.init();
    }

    @Override
    void doSomething() {
        greeter.greet();
    }
}

public class BypassInitializationOrder {
    public static void main(String[] args) {
        new SubClass();
    }
}

Powyższe rozwiązanie daje -666 punktów reputacji u kolegów.

3

A w ogóle czemu chcesz korzystać z dziedziczenia do tego? :|

1 użytkowników online, w tym zalogowanych: 0, gości: 1