Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: anonym 30. 01. 2018, 14:48:32
-
Často jsem v situaci, kdy potřbuju napsat nějakou funkcionalitu. Řekněme, že budu chtít udělat funkcionalitu, která rozdělí 100GB XML na disku na menší XML.
Je jasné, že na toto udělám zvláštní třídu. Ale co už není jasné je, jestli ji udělat stavovou nebo bezestavovou. Např.:
Stavová třída by mohla vypadat takto:
class XMLSplitter {
public XMLSplitter(string inputXMLPath, int size) {}
public IEnumerator<string> getXMLPart() { yield return; }
}
Bezestavová třída takto:
class XMLSplitter {
public XMLSplitter() {}
public IEnumerator<string> getXMLPart(string inputXMLPath, int size) { yield return; }
}
Jenže to už bych mohl rovnou udělat statickou:
static class XMLSplitter {
public static IEnumerator<string> getXMLPart(string inputXMLPath, int size) { yield return; }
}
Stavová verze třídy má problém s tím, že se v ní bude muset resetovat stav při každém opuštění metody getXMLPart(). Navíc, stavovou verzi nelze použít ve více vláknech. Nestavová nebo statická verze tímto problémem netrpí, ale zase trpí tím, že si nemůžu definovat globální proměnné a do každé pomocné metody, kterou budu volat, budu muset propagovat parametry, které by mohly být součástí třídních atributů. To mi příjde, že vizuálně zhoršuje čitelnost kódu.
Tak jak s takovou situací naložit, nějaké návrhy?
-
Kdybych měl odpovědět sám, udělal bych to tak, jak seto dělá v Java Spring. Tzn. všechny Servicy, tj. i můj XMLSplitter, by byly bezestavové. Jediné stavové třídy by byly ty, kterými se přenáší data mezi Service.
Např.
public XMLPart {
string originalFileName;
string content;
string size;
public void SaveToDisc(string location) {}
}
public XMLSplitter() {}
public IEnumerator<XMLPart> getXMLPart(string inputXMLPath, int size) { yield return; }
}
-
Kurnik já když se nad tím teď tak zamýšlím, tak jsem asi skočil do pasti tomu yield return. Protože kdyby moje třída XMLSplitter prostince implementovala Iterator, tak by se o ni automaticky vědělo, že má stav vázaný na aktuální stav třídních atributů a tudíž že ji není možné jen tak recyklovat. Hmmm...
-
že si nemůžu definovat globální proměnné a do každé pomocné metody, kterou budu volat, budu muset propagovat parametry
To není problém.
To mi příjde, že vizuálně zhoršuje čitelnost kódu.
To je otázka zvyku. Posuzovat kvalitu kódu podle počtu argumentů je špatná metrika.
Měl by si se ptát jinak:
- Má smysl, aby ta třída měla více reprezentací?
- Jde paralelizovat?
- Je nutné, aby si udržovala stav?
Obecně je lepší, když si objekt stav nepamatuje. Ano, je to poněkud ústup z původních ideí OOP, ale IMHO se to prostě neosvědčilo. Na druhou stranu jsou věci, které je nesmyslné, až nemožné dělat imutabilní. Například různé akumulátory, loggery, etc. Tedy objekty, jejichž smyslem je různě sbírat data, abych je na konci (nebo i klidně průběžně) zpracoval. Ne, že by to nešlo bez toho, ale někdy je to zbytečně kostrbaté.
Tvůj problém se splitováním velkého množství dat bych udělal jako stavovej, protože stejně potřebuju spolknout celej ten datovej blob, a nějak paralelizovat to stejně nejde.
-
XML splitter bych taky udělal stavově, časem nejspíš přibudou další metody které budou operovat nad stejným xml.
-
@BoneFlute
Ne, že by se to neosvědčilo, ale nebyla možnost něco paralelizovat a tudíž stavovost objektů nebyla na závadu.
-
trida zadnej stav nema a bezstavove OOP nato uz mi uplne dochazi fantazie - je neuveritelny co se da ze slov uvarit
-
@BoneFlute
Ne, že by se to neosvědčilo, ale nebyla možnost něco paralelizovat a tudíž stavovost objektů nebyla na závadu.
Pokud není stav sdílený mezi vlákny tak stavovost objektů nikdy nevadila.
-
No ono ta Java 8 zase takové terno není. Přibyly lambdy, streamy a Optional. Lambdy nejsou zase takové terno. Streamy... viděl jsem, jak to vypadalo, když Indové dostali jasně za úkolo používat streamy, protože je to asi velice profesionální. Vznikl z toho solidní nepřehledné běsy.
Optional taky nic moc, protože to má hrozně ukecanou syntax, viz:
// safe, ugly, omission-prone
if (project != null) {
ApplicationType applicationType = project.getApplicationType();
if (applicationType != null) {
String typeDirName = applicationType.getTypeDirName();
if (typeDirName != null) {
System.out.println(typeDirName);
}
}
}
// let's assume you will get this from your model in the future; in the meantime...
Optional<Project> optionalProject = Optional.ofNullable(project);
// safe, java 8, but still ugly and omission-prone
if (optionalProject.isPresent()) {
ApplicationType applicationType = optionalProject.get().getApplicationType();
Optional<ApplicationType> optionalApplicationType = Optional.ofNullable(applicationType);
if (optionalApplicationType.isPresent()) {
String typeDirName = optionalApplicationType.get().getTypeDirName();
Optional<String> optionalTypeDirName = Optional.ofNullable(typeDirName);
if (optionalTypeDirName.isPresent()) {
System.out.println(optionalTypeDirName);
}
}
// safe, prettier
Optional<String> optionalTypeDirName = optionalProject
.flatMap(project -> project.getApplicationTypeOptional())
.flatMap(applicationType -> applicationType.getTypeDirNameOptional());
optionalTypeDirName.ifPresent(typeDirName -> System.out.println(typeDirName));
A ted to same C#:
var typeDirName = project?.ApplicationType?.TypeDirName;
if(typeDirName != null)
System.Console.WriteLine(typeDirName);
Jinak, jestli někomu z Javistu připadá to použití Streamu jako pěknější a přehlednější, než původní klasické checkování na null, tak by si měl zajít k doktorovi nebo najít holku.
-
Asi bych zvolil střední cestu, při které mohu dělit více XML současně:
class XMLSplitter {
public XMLSplitter(string inputXMLPath) {
}
public IEnumerator<string> getXMLPart(int size) {
// ...
yield return;
}
}
-
A co tohle:
class XMLSplitter : IEnumerator<string> {
XMLSplitter(string inputXMLPath, int size)
string Current();
bool MoveNext();
void Reset();
}
Tím, že třída je IEnumerator je automaticky řečeno, že je stavová, takže s ní nikdo nebude špatně pracovat.
-
...
Upřímně řečeno: Pracovat s XML jako se stringem je docela hloupost. Tohle je práce pro SAX.
-
...
Upřímně řečeno: Pracovat s XML jako se stringem je docela hloupost. Tohle je práce pro SAX.
Proč? To vrací část toho XML jako String. To se pak může někam uložit. Co je na tom špatného?
-
Upřímně řečeno: Pracovat s XML jako se stringem je docela hloupost. Tohle je práce pro SAX.
Proč? To vrací část toho XML jako String. To se pak může někam uložit. Co je na tom špatného?
Riskuješ tím nevalidnost výstupního XML. Nevalidních XML dostáváme hromadu jenom proto, že je někdo neumí generovat.
-
@BoneFlute
Ne, že by se to neosvědčilo, ale nebyla možnost něco paralelizovat a tudíž stavovost objektů nebyla na závadu.
Paralelizovatelnost je nejčastěji skloňovaný důvod. Ale já v praxi spíše narážím na čitelnost a použitelnost. Immutable objekty jsou prostě víc blbuvzdorný. Je-li tam stav, je s tím hrozně práce.
-
Pokud není stav sdílený mezi vlákny tak stavovost objektů nikdy nevadila.
Mě jo.
-
Smím se zeptat, kam ten stav v OOP ukládáte, když ne do objektu? ^^
-
OOP je stavove. Ak je to bezstavove, tak to nie je OOP, ale nieco ine.
-
Smím se zeptat, kam ten stav v OOP ukládáte, když ne do objektu? ^^
Stav se do immutable objektu ukládá, ale jen v konstruktoru.
-
OOP je stavove. Ak je to bezstavove, tak to nie je OOP, ale nieco ine.
S tím dokážu žít.
-
OOP je stavove. Ak je to bezstavove, tak to nie je OOP, ale nieco ine.
OOP je přece hlavně o tom, když to porovnám s procedurálním strukturovaným programováním (třeba v C), že ti přímo umožňuje nad moduly (třídy) provádět polymorfismus. To je nejdůležitější vlastnost. Druhá vlastnost je, že máš přimou podporu pro zapouzřenost (pomocí přístupových modifikátorů), což by měl každý modul splňovat. Nejméně důležitá vlastnost je pak ta, že ti umožňuje vytvářet moduly, které rozšiřují nějaké moduly (dědičnost).
Jestli budeš dělat objekty stavové nebo bezestavové, to je pak už úplně podružné.
-
Druhá vlastnost je, že máš přimou podporu pro zapouzřenost (pomocí přístupových modifikátorů), což by měl každý modul splňovat.
Přístupové modifikátory porušují zapouzdřenost, proto by se neměly používat. Atributy objektu mají být privátní, mají s nimi pracovat pouze metody objektu. Okolí by vůbec nemělo vědět, jaké má objekt atributy. Měly by být zcela skryty, tedy zapouzdřeny.
-
hasismarja
-
hasismarja
To je nějaký nový typ bezstavového objektu? :D
-
Druhá vlastnost je, že máš přimou podporu pro zapouzřenost (pomocí přístupových modifikátorů), což by měl každý modul splňovat.
Přístupové modifikátory porušují zapouzdřenost, proto by se neměly používat. Atributy objektu mají být privátní, mají s nimi pracovat pouze metody objektu. Okolí by vůbec nemělo vědět, jaké má objekt atributy. Měly by být zcela skryty, tedy zapouzdřeny.
A co metody? Navíc, jak budeš dělat mocky, když atributům nedáš gettery a settery? Můžeš je sice vytáhnou reflexí, ale potom zase ztratíš možnost refaktrorovat názvy atributů, aniž by jsi musel všechny unit testy upravit.
-
Druhá vlastnost je, že máš přimou podporu pro zapouzřenost (pomocí přístupových modifikátorů), což by měl každý modul splňovat.
Přístupové modifikátory porušují zapouzdřenost, proto by se neměly používat. Atributy objektu mají být privátní, mají s nimi pracovat pouze metody objektu. Okolí by vůbec nemělo vědět, jaké má objekt atributy. Měly by být zcela skryty, tedy zapouzdřeny.
A co metody? Navíc, jak budeš dělat mocky, když atributům nedáš gettery a settery? Můžeš je sice vytáhnou reflexí, ale potom zase ztratíš možnost refaktrorovat názvy atributů, aniž by jsi musel všechny unit testy upravit.
K čemu reflexe? Názvy atributů mohu měnit dle libosti, aniž by to mělo na unit testy i na zbytek aplikace sebemenší vliv, protože je prostě nevidí. Testy testují pouze rozhraní třídy, atributy se v něm nevyskytují. Mocky nastrkám do konstruktoru a jako parametry metod. Jinde je nepotřebuji.
-
Druhá vlastnost je, že máš přimou podporu pro zapouzřenost (pomocí přístupových modifikátorů), což by měl každý modul splňovat.
Přístupové modifikátory porušují zapouzdřenost, proto by se neměly používat. Atributy objektu mají být privátní, mají s nimi pracovat pouze metody objektu. Okolí by vůbec nemělo vědět, jaké má objekt atributy. Měly by být zcela skryty, tedy zapouzdřeny.
A co metody? Navíc, jak budeš dělat mocky, když atributům nedáš gettery a settery? Můžeš je sice vytáhnou reflexí, ale potom zase ztratíš možnost refaktrorovat názvy atributů, aniž by jsi musel všechny unit testy upravit.
K čemu reflexe? Názvy atributů mohu měnit dle libosti, aniž by to mělo na unit testy i na zbytek aplikace sebemenší vliv, protože je prostě nevidí. Testy testují pouze rozhraní třídy, atributy se v něm nevyskytují. Mocky nastrkám do konstruktoru a jako parametry metod. Jinde je nepotřebuji.
Jak jako Mocky nastrkáš do konstruktoru. Co když se ve třídě vytváří objekty jiné třídy a ty je budeš musel v unit testu namockovat, jinak se ti zavolají. To jako uděláš jeden velký zbytečný konstruktor, který bude obsahovat všechny vytvářené instance ve třídě? Wtf. Pošli nějaký svůj kód na github, já bych to chtěl vidět.Nebo nějakou tvojí utilitu pro vim, jestli se nestydíš :D
-
K čemu reflexe? Názvy atributů mohu měnit dle libosti, aniž by to mělo na unit testy i na zbytek aplikace sebemenší vliv, protože je prostě nevidí. Testy testují pouze rozhraní třídy, atributy se v něm nevyskytují. Mocky nastrkám do konstruktoru a jako parametry metod. Jinde je nepotřebuji.
Jak jako Mocky nastrkáš do konstruktoru. Co když se ve třídě vytváří objekty jiné třídy a ty je budeš musel v unit testu namockovat, jinak se ti zavolají.
To se dělá tak, že se ty objekty jiné třídy injektují do konstruktoru nebo do parametru metody. Zjednoduší se tím design třídy a zlepší robustnost aplikace.
To jako uděláš jeden velký zbytečný konstruktor, který bude obsahovat všechny vytvářené instance ve třídě? Wtf.
Ne. Do konstruktoru injektuji již vytvořené a nakonfigurované vnější objekty. Konstruktor si je pouze uloží mezi atributy. Tím se rapidně zlepšuje znovupoužitelnost třídy a polymorfismus zde získává ty správné možnosti.
-
trida zadnej stav nema a bezstavove OOP nato uz mi uplne dochazi fantazie - je neuveritelny co se da ze slov uvarit
Trida muze mit stav reprezentovany statickymi atributy ne? Tedy kdyz se bavime v kontextu Javy.
-
K čemu reflexe? Názvy atributů mohu měnit dle libosti, aniž by to mělo na unit testy i na zbytek aplikace sebemenší vliv, protože je prostě nevidí. Testy testují pouze rozhraní třídy, atributy se v něm nevyskytují. Mocky nastrkám do konstruktoru a jako parametry metod. Jinde je nepotřebuji.
Jak jako Mocky nastrkáš do konstruktoru. Co když se ve třídě vytváří objekty jiné třídy a ty je budeš musel v unit testu namockovat, jinak se ti zavolají.
To se dělá tak, že se ty objekty jiné třídy injektují do konstruktoru nebo do parametru metody. Zjednoduší se tím design třídy a zlepší robustnost aplikace.
To jako uděláš jeden velký zbytečný konstruktor, který bude obsahovat všechny vytvářené instance ve třídě? Wtf.
Ne. Do konstruktoru injektuji již vytvořené a nakonfigurované vnější objekty. Konstruktor si je pouze uloží mezi atributy. Tím se rapidně zlepšuje znovupoužitelnost třídy a polymorfismus zde získává ty správné možnosti.
WTF! Tak tady máš frajere a ukaž se, jak otesteješ (a nebo upravíš) třídu Program a metodu Run().
public class Program
{
OptionSet o;
// Console params
public String FeedFilePath { get; set; }
public String TargetLocation { get; set; }
public int? TestFileCount { get; set; }
public bool Help { get; set; } = false;
public int? BatchSize { get; set; }
public IProcessor TradeProcessor { get; set; }
public Tester Tester { get; set; }
public Program(params string[] args)
{
LoggerSetting.init();
o = new OptionSet()
{
{ "f|filePath=", "the input {FILE} path of Trade datafeed. Defaul is current dir + '/TradesList.xml'.", v => FeedFilePath = v },
{ "t|targetPath=", "the {DESTINATION} path. Default is current dir.", v => TargetLocation = v },
{ "g|testFile=", "generate TradesList.xml of {COUNT} elements in current dir.", v => TestFileCount = Int32.Parse(v) },
{ "b|batchSize=", "Batch {SIZE}. Default is 1000.", v => BatchSize = Int32.Parse(v) },
{ "h|help", "Show this help.", v => Help = v != null}
};
List<string> extra;
try
{
extra = o.Parse(args);
}
catch (OptionException e)
{
Console.Write("datafeeds: ");
Console.WriteLine(e.Message);
Console.WriteLine("Try `--help' for more information.");
return;
}
if (Help)
{
printHelp();
return;
}
if (FeedFilePath == null)
{
FeedFilePath = AppDomain.CurrentDomain.BaseDirectory + "/TradesList.xml";
}
if (TargetLocation == null)
{
TargetLocation = AppDomain.CurrentDomain.BaseDirectory;
}
if (BatchSize == null)
{
BatchSize = 1000;
}
TradeProcessor = new TradeProcessor(this.FeedFilePath, this.TargetLocation, BatchSize.Value);
Tester = new CTSTestApplication.Tester();
}
public void run()
{
if(TestFileCount.HasValue)
{
Tester.CreateTestFile(AppDomain.CurrentDomain.BaseDirectory, TestFileCount.Value);
}
if (TradeProcessor != null)
{
TradeProcessor.Process();
}
}
private void printHelp()
{
Console.WriteLine("help");
o.WriteOptionDescriptions(Console.Out);
}
public static void Main(string[] args)
{
Program program = new Program(args);
program.run();
}
}
[TestClass()]
public class ProgramTests
{
TradeProcessorMock tradeProcessorMock = new TradeProcessorMock();
[TestMethod()]
public void ProgramTest()
{
string path = AppDomain.CurrentDomain.BaseDirectory;
string s = "-filePath=" + path;
string s2 = "-targetPath=" + path;
string s3 = "-testFile=150";
string s4 = "-batchSize=999";
Program p = new Program(s, s2, s3, s4);
Assert.AreEqual(p.FeedFilePath, path);
Assert.AreEqual(p.TargetLocation, path);
Assert.AreEqual(p.TestFileCount, 150);
Assert.AreEqual(p.BatchSize, 999);
p.TradeProcessor = tradeProcessorMock;
p.TestFileCount = null; // Can't mock Tester
p.run();
Assert.AreEqual(tradeProcessorMock.ProcessRunCount, 1);
}
-
WTF! Tak tady máš frajere a ukaž se, jak otesteješ (a nebo upravíš) třídu Program a metodu Run().
Metoda Run() tam není. Kromě toho je to v C#, ve kterém nedělám.
-
WTF! Tak tady máš frajere a ukaž se, jak otesteješ (a nebo upravíš) třídu Program a metodu Run().
Metoda Run() tam není. Kromě toho je to v C#, ve kterém nedělám.
Tak to udělej třeba v Javě, napiš ekvivalent te tridy Program. Chci videt, jak tam TradeProcessor a Test strkas pres ten konstruktor.
-
Tak to udělej třeba v Javě, napiš ekvivalent te tridy Program. Chci videt, jak tam TradeProcessor a Test strkas pres ten konstruktor.
Dělám teď na něčem jiném, ale vidím, že s tím testem na null jen obcházíš Elvise. To v Javě uděláš tak, že definuješ hodnotu atributu už při vytvoření instance objektu. Při zadání patřičného parametru ji změníš.
Uniká mi, co má dělat ten TradeProcessor, ale zběžně tam místo new TradeProcessor() strčím v testu new MockTradeProcessor().
-
Tak to udělej třeba v Javě, napiš ekvivalent te tridy Program. Chci videt, jak tam TradeProcessor a Test strkas pres ten konstruktor.
Dělám teď na něčem jiném, ale vidím, že s tím testem na null jen obcházíš Elvise. To v Javě uděláš tak, že definuješ hodnotu atributu už při vytvoření instance objektu. Při zadání patřičného parametru ji změníš.
Uniká mi, co má dělat ten TradeProcessor, ale zběžně tam místo new TradeProcessor() strčím v testu new MockTradeProcessor().
Tohle začíná být už opravdu hodně zoufalé, mírně řečeno. Jak tam strčíš v testu new MockTradeProcessor(), když nebudeš mít setter nad atributem TradeProcessor ve třídě Program?
Obecně řečeno, pokud v nějaké třídě A musíš speciálně vytvořit instanci jiné třídy B a C, tak dpč. asi těžko uděláš konstruktor, který bere třídy B a C. Testovat to, dpč., musíš buďto tak, že tam dáš settery nebo to nasetuješ přes reflexi. A pokud to nasetuješ přes reflexi, příjdeš o automatizovanou refaktorizaci.
Konec diskuzí s tebou, začínám tě považovat za trolla autistického typu.
-
Obecně řečeno, pokud v nějaké třídě A musíš speciálně vytvořit instanci jiné třídy B a C, tak dpč. asi těžko uděláš konstruktor, který bere třídy B a C. Testovat to, dpč., musíš buďto tak, že tam dáš settery nebo to nasetuješ přes reflexi. A pokud to nasetuješ přes reflexi, příjdeš o automatizovanou refaktorizaci.
Ty objekty tříd B a C si vytvořím mimo třídu A. Pak jen zavolám A a = new A(b, c);
Místo parametrů b a c si kdykoli mohu namockovat něco jiného.
-
Tohle začíná být už opravdu hodně zoufalé, mírně řečeno. Jak tam strčíš v testu new MockTradeProcessor(), když nebudeš mít setter nad atributem TradeProcessor ve třídě Program?
c sharp neznám. proč musíte psát get, set když je to public?
v moderním javascriptu by to šlo nějak tak
p = {...new Program(s, s2, s3, s4), TradeProcessor : tradeProcessorMock}
-
c sharp neznám. proč musíte psát get, set když je to public?
Takhle si programátoři v C# představují zapouzdření.
-
Smím se zeptat, kam ten stav v OOP ukládáte, když ne do objektu? ^^
Stav se do immutable objektu ukládá, ale jen v konstruktoru.
Jak to souvisí s "bezestavovým" OOP?
-
Smím se zeptat, kam ten stav v OOP ukládáte, když ne do objektu? ^^
Stav se do immutable objektu ukládá, ale jen v konstruktoru.
Jak to souvisí s "bezestavovým" OOP?
Měl jsem tedy položenou otázku ponechat bez odpovědi? Tady se člověk prostě nezavděčí...
-
To jako uděláš jeden velký zbytečný konstruktor, který bude obsahovat všechny vytvářené instance ve třídě? Wtf.
Ne. Do konstruktoru injektuji již vytvořené a nakonfigurované vnější objekty. Konstruktor si je pouze uloží mezi atributy. Tím se rapidně zlepšuje znovupoužitelnost třídy a polymorfismus zde získává ty správné možnosti.
Taky, ale jde to např. i pomocí uzávěr předaných mocku, které ve správný okamžik vykotí, nebo jinak obstarají požadovaný objekt. A zatřetí některé vlastnosti v mocku mohou mít přístupové metody, dává-li to smysl, se samotným OOP se to nevylučuje.
-
c sharp neznám. proč musíte psát get, set když je to public?
Takhle si programátoři v C# představují zapouzdření.
Get a set v C# není nic jiného než syntaktický cukr pro jakékoliv, ale především přístupové metody - jsou zbytné. Jestli to (jako Javař?) nevíte, tak vám to sděluju.
-
Tak to udělej třeba v Javě, napiš ekvivalent te tridy Program. Chci videt, jak tam TradeProcessor a Test strkas pres ten konstruktor.
Dělám teď na něčem jiném, ale vidím, že s tím testem na null jen obcházíš Elvise. To v Javě uděláš tak, že definuješ hodnotu atributu už při vytvoření instance objektu. Při zadání patřičného parametru ji změníš.
Uniká mi, co má dělat ten TradeProcessor, ale zběžně tam místo new TradeProcessor() strčím v testu new MockTradeProcessor().
Tohle začíná být už opravdu hodně zoufalé, mírně řečeno. Jak tam strčíš v testu new MockTradeProcessor(), když nebudeš mít setter nad atributem TradeProcessor ve třídě Program?
Obecně řečeno, pokud v nějaké třídě A musíš speciálně vytvořit instanci jiné třídy B a C, tak dpč. asi těžko uděláš konstruktor, který bere třídy B a C. Testovat to, dpč., musíš buďto tak, že tam dáš settery nebo to nasetuješ přes reflexi. A pokud to nasetuješ přes reflexi, příjdeš o automatizovanou refaktorizaci.
Konec diskuzí s tebou, začínám tě považovat za trolla autistického typu.
Předpokládám, že vidíš problém v tom, že ten TradeProcessor krmíš daty, které jsou k dispozici až v tom Program, že? Tak je fakt, že něco takového tam injektovat moc nemá smysl, máš-li to postavené takhle.
Každopádně moc nechápu, proč se tady rozčiluješ mezi konstruktorem versus setterem, versus reflexi? Vždyť to nic neřeší.
Ten TradeProcessor tam nemůžeš namockovat ani setterem, ani reflexí.
Rozdíl mezi konstruktorem a setterem je jen a pouze v tom, že konstruktorem hodnoty nastavuješ povinně, zatímco setterem volitelně.
-
The Shits
-
Ten TradeProcessor tam nemůžeš namockovat ani setterem, ani reflexí.
Rozdíl mezi konstruktorem a setterem je jen a pouze v tom, že konstruktorem hodnoty nastavuješ povinně, zatímco setterem volitelně.
Proč to tam nemůžu namockovat setterem, vždyť jsem to udělal. Rozdělil jsem práci se třídou logicky na 2 kroky, v 1. se dělá veškerá inicializace, až ve 2. kroku se cokoliv spouští. Díky tomu můžu přes settery přpsat inicializované instance tříd dle libosti mocky.
-
Ten TradeProcessor tam nemůžeš namockovat ani setterem, ani reflexí.
Rozdíl mezi konstruktorem a setterem je jen a pouze v tom, že konstruktorem hodnoty nastavuješ povinně, zatímco setterem volitelně.
Proč to tam nemůžu namockovat setterem, vždyť jsem to udělal. Rozdělil jsem práci se třídou logicky na 2 kroky, v 1. se dělá veškerá inicializace, až ve 2. kroku se cokoliv spouští. Díky tomu můžu přes settery přpsat inicializované instance tříd dle libosti mocky.
A, máš pravdu, přehlédl jsem se. No tak pak je to jednoduché. Místo setteru to tam můžeš předat zkrzeva konstruktor. Setter v tomto případě nedává žádný smysl.
Teda, jestli tvé uvažování chápu správně, tak ten setter máš jen kůli tomu, aby si mohl mockovat. Chápu. No, mě to nepřijde vůbec hezké. Je to funkční, ale takové nepřímočaré.
-
Ha, takže já můžu udělat v C# tohle:
class Program
{
private int Orechy;
private int Svestky;
private int Cigara;
static void Main(string[] args)
{
Program p = new Program
{
Orechy = 10,
Svestky = 50,
Cigara = 20
};
}
}
To je brutální :) V Javě, když jsme potřebovali dělat unit testy, tak se na všechny různé fieldy psaly gettery a settery, abychom se k tomu dostali.
-
No takže nic, funguje to jen zevnitř třídy, takže jsem opět u getteru a setteru nebo u konstruktoru...
-
Zase taková pitomá odpověď na stackoverflow
he quick answer is that you should never, ever access non-public members from your unit tests. It totally defies the purpose of having a test suite, since it locks you into internal implementation details that you may not want to keep that way.
https://stackoverflow.com/questions/1093020/unit-testing-and-checking-private-variable-value
Unit test má testovat jenom flow v nějaké metodě. Jak jí má ale otestovat bez přístupu k private fieldům, když je to třída vytvářecího typu, jako je moje třída Program?
V podstatě pokud mám nějakou třídu B, která interně vytváří C a já musím C namokovat, tak prostě MUSÍM mít přístup k C. Přinejmenším přes reflexi, která je ale naprd, protože hardkoduje nazvy fieldu.
To jsou fakt tohleto poučky jak z nějaké příručky univerzitního profesora v brýlích co nikdy nic nenaprogramoval.
-
Já si myslím, že můj přístup s get a set je v případě mé třídy Program správný. Ta třída Program dělá to, že na základě agumentů konzole konfiguruje spuštění nějakých procesů. Je správné, že má gettery a settery na to, co nakonfigurovala, takže to není porušení zapouzdřenosti.
-
No takže nic, funguje to jen zevnitř třídy, takže jsem opět u getteru a setteru nebo u konstruktoru...
V Javě se na to používá statická vnitřní třída. Výsledek je v jiném souboru, ale má přístup k privátním proměnným vnějšího objektu. Používám to.
-
Rozdíl mezi konstruktorem a setterem je jen a pouze v tom, že konstruktorem hodnoty nastavuješ povinně, zatímco setterem volitelně.
Netýká se to moc tématu, ale tak pro zajímavost - velký rozdíl mezi konstruktorem a setterem je ten, že pomocí konstruktrou nelze vytvářet cyklické struktury (objekt A referencuje B, B referencuje A).
-
Tohle je fakt psina. Neustále se omílají mantry o tom, jak vysokoúrovňové jazyky a v současné době propagovaná paradigmata svou vyšší abstrakcí umožňují soustředit se na řešení problému a ne na věci okolo, jak tomu prý bývalo dříve, ale přitom se tu neustále rozebírá, jak kdejakou trivialitu správně napsat, aby to jako vyhovovalo těm teoretickým představám a návrhovým vzorům, jimž byl ten konkrétní jazyk přizpůsoben.
-
Ty vole já se schálně přemůžu a napíšu ten samý program na parsovální 100gb xmlka do databáze v Céčku. Fakt to udělám. Schválně, co bude přehlednější a v čem se bude líp dělat.
-
Fuj tak raději ne, vzdávám se.
/*
A simple test program to parse XML documents with expat
<http://expat.sourceforge.net/>. It just displays the element
names.
On Debian, compile with:
gcc -Wall -o expat-test -lexpat expat-test.c
Inspired from <http://www.xml.com/pub/a/1999/09/expat/index.html>
*/
#include <expat.h>
#include <stdio.h>
#include <string.h>
/* Keep track of the current level in the XML tree */
int Depth;
#define MAXCHARS 1000000
void
start(void *data, const char *el, const char **attr)
{
int i;
for (i = 0; i < Depth; i++)
printf(" ");
printf("%s", el);
for (i = 0; attr[i]; i += 2) {
printf(" %s='%s'", attr[i], attr[i + 1]);
}
printf("\n");
Depth++;
} /* End of start handler */
void
end(void *data, const char *el)
{
Depth--;
} /* End of end handler */
int
main(int argc, char **argv)
{
char *filename;
FILE *f;
size_t size;
char *xmltext;
XML_Parser parser;
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
return (1);
}
filename = argv[1];
parser = XML_ParserCreate(NULL);
if (parser == NULL) {
fprintf(stderr, "Parser not created\n");
return (1);
}
/* Tell expat to use functions start() and end() each times it encounters
* the start or end of an element. */
XML_SetElementHandler(parser, start, end);
f = fopen(filename, "r");
xmltext = malloc(MAXCHARS);
/* Slurp the XML file in the buffer xmltext */
size = fread(xmltext, sizeof(char), MAXCHARS, f);
if (XML_Parse(parser, xmltext, strlen(xmltext), XML_TRUE) ==
XML_STATUS_ERROR) {
fprintf(stderr,
"Cannot parse %s, file may be too large or not well-formed XML\n",
filename);
return (1);
}
fclose(f);
XML_ParserFree(parser);
fprintf(stdout, "Successfully parsed %i characters in file %s\n", size,
filename);
return (0);
}
-
Nebylo by na to lepší použít nějaký standardní XSLT nástroj?
-
Rozdíl mezi konstruktorem a setterem je jen a pouze v tom, že konstruktorem hodnoty nastavuješ povinně, zatímco setterem volitelně.
Netýká se to moc tématu, ale tak pro zajímavost - velký rozdíl mezi konstruktorem a setterem je ten, že pomocí konstruktrou nelze vytvářet cyklické struktury (objekt A referencuje B, B referencuje A).
No to platí v javě, protože konstruktor lze použít jen jednou a java používá Call_by_value (https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value) takže zvnějšku už to nezměním.
Ale platí to i v C#? Když tam zdá se můžeme použít i Předávání parametrů odkazem (ref) (https://docs.microsoft.com/cs-cz/dotnet/csharp/programming-guide/classes-and-structs/passing-parameters) takže bych mohl nastavit prázdný ukazatel jako výstupní parametr a ten pak dodatečně změnit?
?? (C# neznám...)
-
Tohle je fakt psina. Neustále se omílají mantry o tom, jak vysokoúrovňové jazyky a v současné době propagovaná paradigmata svou vyšší abstrakcí umožňují soustředit se na řešení problému a ne na věci okolo, jak tomu prý bývalo dříve, ale přitom se tu neustále rozebírá, jak kdejakou trivialitu správně napsat, aby to jako vyhovovalo těm teoretickým představám a návrhovým vzorům, jimž byl ten konkrétní jazyk přizpůsoben.
Přesně z toho důvodu jsem se na OO před pár lety vyprd a utekl jsem k FP, jmenovitě F# a teď trochu Elixir(ten se mi moc líbí) a Clojure (s tím si zatím jen hraju, protože jsem se vždycky chtěl naučit LISP, ale nebyl na to čas. LISP nad JVM je něco, co se může někdy hodit).
-
...V Javě, když jsme potřebovali dělat unit testy, tak se na všechny různé fieldy psaly gettery a settery, abychom se k tomu dostali.
A je to skutečně potřeba? Jednotkovými testy se přece testuje správnost objektu navenek, ne uvnitř - funguje-li navenek, je v pořádku. Naopak čuměním jedn. testy dovnitř porušujete zapouzdření, které platí i zde, tj. nezávislost vnitřku a venku. Mimoto je možno vytvořit objektu metodu, která si ověří vlastní správnost stavů, případně i v kombinaci se závislými objekty, a zahlásí rozpor (používám běžně), aniž by bylo třeba zapouzdření porušit.
Myslím si, že na to nejdete dobře.
-
...V Javě, když jsme potřebovali dělat unit testy, tak se na všechny různé fieldy psaly gettery a settery, abychom se k tomu dostali.
A je to skutečně potřeba? Jednotkovými testy se přece testuje správnost objektu navenek, ne uvnitř - funguje-li navenek, je v pořádku. Naopak čuměním jedn. testy dovnitř porušujete zapouzdření, které platí i zde, tj. nezávislost vnitřku a venku. Mimoto je možno vytvořit objektu metodu, která si ověří vlastní správnost stavů, případně i v kombinaci se závislými objekty, a zahlásí rozpor (používám běžně), aniž by bylo třeba zapouzdření porušit.
Myslím si, že na to nejdete dobře.
Jednotkovými testy se skutečně testuje jen chování objektu navenek. Jenže jsou zde ještě vývojářské testy, které se zaměřují na vnitřní chování objektu. Za tím účelem se hodí statická vnitřní třída, ve které se dají psát tyto vývojářské testy, které mají přístup k privátním proměnným i metodám.